Разбор текстового файла с разделителями в 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

Я полагаю, что команда, которую он запускает, читает стандартный ввод, прежде чем команда «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.


1

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

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

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

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


1

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.