Разделить вывод команды по переносу строки?

9

У меня есть команда, возвращающая несколько строк. Для дальнейшей обработки мне нужно обработать каждую строку этих строк.

Мой текущий код работает путем изменения IFS ( Разделитель внутренних полей ):

ROWS=$(some command returning multiple lines)

O=$IFS #save original IFS
IFS=$(echo -en "\n\b") # set IFS to linebreak

for ROW in ${ROWS[@]}
do
  echo "$ROW"
done

IFS=$O #restore old IFS

Мне интересно, есть ли лучший способ получить доступ к отдельным строкам вывода нескольких строк, один без изменения IFS? Особенно читаемость моего сценария ухудшается при модификации IFS.

Обновление: у меня проблемы с получением ответов, например, от Чороба:

while IFS= read -r line ; do
    let var+=line #line 42
done << $(sqlite3 -list -nullvalue NULL -separator ',' /var/log/asterisk/master.db "${QUERY}")
echo "$var" # line 44

дает мне

./bla.sh: row 44: Warning: here-document at line 43 delimited by end-of-file (wanted `$(sqlite3 -list -nullvalue NULL -separator , /var/log/asterisk/master.db ${QUERY})')
./bla.sh: row 42: let: echo "": syntax error: invalid arithmetic operator. (error causing character is \"""\").

Кто-нибудь может мне помочь с этим? Спасибо!

stefan.at.wpf
источник
ответ Чороба говорит, что делать < <(some command returning multiple lines), но это не то, что вы делаете.
Микель

Ответы:

13

Как насчет readв whileцикле?

some command returning multiple lines | while IFS= read -r line ; do
    echo "$line"
done

Однако будьте осторожны, так как канал запускается в подоболочке, что означает, что вы не можете изменять значения переменных для остальной части сценария. Если вам нужно что-то, чтобы выжить из цикла while, вы можете использовать замену процесса в bash:

while IFS= read -r line ; do
    let var+=line
done < <(some command returning multiple lines)
echo "$var"
choroba
источник
Использование процесса подстановки - разумное решение для этого bash, но стоит отметить, что определение переменных в скорлупе является лишь проблемой bash, так как выполнение того же действия zshвообще не будет иметь этой проблемы.
Калеб
Кш тоже не имеет этой проблемы.
Диди Кохен
спасибо, ребята, я попробовал это решение, но я не могу заставить его работать, пожалуйста, смотрите мой обновленный оригинальный пост, спасибо!
stefan.at.wpf
@ stefan.at.wpf: Вы не можете размещать пробелы и знаки доллара по желанию. У них есть смысл.
Чороба
3

Установка IFSтолько новой строки недостаточно. (Кстати, почему вы тоже разделяете символы возврата на задний план?)

В вашем коде ${ROWS[@]}(что странно писать $ROWS- ROWSэто не массив) не стоит заключать в двойные кавычки. (Если бы он был внутри двойных кавычек, вы бы получили одну строку, поскольку ROWSэто не массив.) Таким образом, оболочка разбивает значение переменной на поля в каждом IFSсимволе, а затем обрабатывает каждое поле как шаблон глобуса. Например, если одна из строк, напечатанных командой, содержит один символ *, он будет заменен именами файлов в текущем каталоге.

Вы можете отключить с помощью set -f. В большинстве случаев, когда вы IFSиспользуете функцию разделения поля оболочки, вам также необходимо отключить глобализацию. Установите его обратно с помощью set +f.

Надежная идиома для чтения выходных данных команды построчно while IFS= read -r.

some command returning multiple lines |
while IFS= read -r ROW; do
  
done

Обратите внимание, что большинство оболочек запускают каждую команду конвейера в отдельной подоболочке. Поэтому, если вам нужно установить переменные и использовать их после цикла, оберните эти команды в группу вместе с циклом. (Ksh и zsh - исключения, они запускают последнюю команду конвейера в родительской оболочке.)

some command returning multiple lines | {
  while IFS= read -r ROW; do
    
    row_count=$((row_count+1))
  done
  echo "There were $row_count rows."
}
Жиль "ТАК - перестань быть злым"
источник
1

Вы смешиваете синтаксис here-document и here-string в своем вопросе об обновлении.

Либо используйте здесь-документ:

while IFS= read -r line ; do
    let var+=line #line 42
done <<ENDMARK
$(sqlite3 -list -nullvalue NULL -separator ',' /var/log/asterisk/master.db "${QUERY}")
ENDMARK

Или вот строчка:

while IFS= read -r line ; do
    let var+=line #line 42
done <<< $(sqlite3 -list -nullvalue NULL -separator ',' /var/log/asterisk/master.db "${QUERY}")
manatwork
источник