Разбор текстового файла с разделителями в bash в качестве аргументов команды

10

У меня есть текстовый файл разделен так:

field1,field2,field3 
xield1,xield2,xield3 
dield1,dield2,dield3 
gield1,gield2,gield3

Каждый из этих столбцов будет параметром программы, и я хотел бы, чтобы программа вызывалась для каждой строки.

Я надеялся на цикл, что-то вроде:

for $i in file
    command $field2 -x $field3 -PN -$field1 >> output
done

Каков наилучший способ сделать что-то подобное в bash?

декан
источник
Является ли количество полей постоянным?
Джозеф Р.
@JosephR. да, они всегда 3
декан

Ответы:

7
while IFS=, read xx yy zz;do
    echo $xx $yy $zz
done < input_file

Это должно работать, если количество полей постоянное. Вместо того, чтобы echoиспользовать вашу команду.

coffeMug
источник
Спасибо, я только пробовал это, но это только работает для первой линии. Как только команда преуспевает, она не пробует следующую, если она терпит неудачу, она пробует следующую, хотя ...
Декан
Как вы подразумеваете под успехом или неудачей? Что делает ваша команда?
coffeMug
Я полагаю, что команда, которую он запускает, читает стандартный ввод, прежде чем команда «read» сможет его найти.
plugwash
4

Вы должны использовать whileсо readвстроенным:

while IFS= read -r line;do
    fields=($(printf "%s" "$line"|cut -d',' --output-delimiter=' ' -f1-))
    command "${fields[1]}" -x "${fields[2]}" ... # ${fields[1]} is field 2
done < your_file_here

Как это работает

  • cutОператор принимает строку и разбивает ее на ограничителе , указанного -d.
  • Символ --output-delimiter-разделитель, который cutбудет использоваться для отображения выбранных полей, здесь мы выбираем пробел, чтобы мы могли поместить различные поля в массив fields.
  • Наконец, мы хотим, чтобы все поля (от поля 1 до конца) и вот где -f1-вступает в игру.
  • Теперь у вас есть различные поля , хранящиеся в переменной массива fields, вы можете получить доступ к любой конкретной области вы хотите с синтаксисом , ${field[number]}где numberодин меньше , чем фактическое количество поля вы хотите , так как индексация массива начинается с нуля в Bash.

Заметка

  • Это не удастся, если любое из ваших полей содержит пробелы.

Для постоянного количества полей

Вместо этого вы можете сделать что-то похожее на ответ 1_CR :

while IFS= read -r line;do
    IFS=, read -r field1 field2 field3 <<-EOI
    $line
    EOI
    command "$field2" -x "$field3" ... 
done < your_file_here

Вышеуказанное, хотя и выглядит более шумно, должно работать в любой POSIX-совместимой оболочке, а не только в Bash.

Джозеф Р.
источник
Это не чтение в файле, с которым у меня возникли проблемы, это разбивка строки на столбцы.
Декан
@ Дин Да, прости. Я не обращал внимания. Работаю над этим сейчас.
Джозеф Р.
@ Декан Пожалуйста, смотрите обновленный ответ. Я добавлю объяснение в ближайшее время.
Джозеф Р.
@JosephR., Можно избежать использования внешних инструментов для разделения, установив IFSсоответствующее значение в readвызове
iruvar
@ 1_CR Я знаю, спасибо. Я только что дошел до этого :)
Джозеф Р.
1

Вы можете readразбить каждую строку на массив ,, установив IFSсоответствующим образом.

while IFS=, read -r -a input; do
 printf "%s\n" "${input[0]}" "${input[1]}"
done < input.txt

Таким образом, в приведенном выше примере вы можете получить доступ к каждому элементу массива, используя его индекс, начиная с 0.

Iruvar
источник
1

Этот awkоднострочник будет делать то, что вы хотите:

awk -F, '{cmd="echo " $2 " -x " $3 " -PN " $1 ">> output";  system(cmd)}' f.txt

Замените его echoкомандой и f.txtфайлом, который вы хотите перебрать.

Краткое объяснение: -F,будет установлен ,в качестве разделителя. cmdстроит команду и system(cmd)вызывает команду.

MKC
источник
1

Также можно использовать GNU SED.

sed infile -e 's!^\([^,]*\),\([^,]*\),\([^,]*\)$!command \1 -x \2 -PN \3!e' >> output

обратите внимание на использование опции e для команды s

hildred
источник