Обработка ввода строки за строкой (с пустыми строками) из оболочки

В сценарии оболочки мне нужно проанализировать вывод команды по очереди. Выходные данные могут содержать пустые строки, и это актуально. Я использую золу, а не bash, поэтому не могу прибегнуть к замене процесса. Я стараюсь:

OUT=`my_command` IFS=$'\n' i=1 for line in $OUT; do echo $line eval VAL$i=$line i=$((i+1)) done 

Однако это отбрасывает пустые строки в $ OUT. Как я могу исправить это, чтобы пустые строки также обрабатывались?

  • Что означает «если » означает? Где $ name - путь к каталогу
  • Как разделить «однострочный текст» на основе шаблона?
  • Как «отправить» переменную в под-оболочку?
  • Сценарий оболочки для логротации
  • Скопировать файлы на основе дат в имени файла
  • Как сжимается тест -eq выражение с более чем одним значением var в / bin / sh
  • объединение вывода команды grep
  • найти максимальное значение столбца 1 и распечатать соответствующую запись из столбца 2 из файла
  • 3 Solutions collect form web for “Обработка ввода строки за строкой (с пустыми строками) из оболочки”


    Рабочий цикл оболочки может выглядеть как …

     set -f -- "-$-"' -- "$@" '" ${IFS+IFS=\$2} ${out+out=\$3}" \ "$IFS" "$out" "$@" IFS=' ';for out in $(my command|grep -n '.\|') do : something with "${out%%:*}" and "${out#*:}" done unset IFS out eval "set +f $1" shift 3 

    Вам нужно только организовать его, чтобы не было никаких пустых строк. Хотя я и предложил nl для этой цели, во-вторых, есть небольшая вероятность того, что логический разделитель страницы nl может произойти во входе и исказить его выход (это приведет к тому, что на самом деле будет пустая строка и повлияет на эту строку был пронумерован – это очень удобная функция для других целей, хотя) . Кроме интерпретации логических разрывов страниц, grep -n '.\|' результаты будут одинаковыми.

    Используя подобный конвейер с небольшой подстановкой параметров, и вы можете не только избежать проблемы с пустой строкой, но и каждая итерация будет пронумерована одновременно – (текущий номер итерации теперь будет во главе каждого значения, которое вам было для $out за которым следует : .

    set ... IFS=... строки, чтобы гарантировать, что состояние оболочки будет восстановлено до того места, где вы оставили его, прежде чем изменять его. Эти меры предосторожности могут быть чрезмерными, если это сценарий, а не функция. Тем не менее, вы должны по крайней мере set -f перед set -f оболочки, чтобы избежать непреднамеренного подталкивания на вашем входе.


    Но о (d)ash и <( замещение процесса )

    Опять же, в полученном busybox ash Debian ( dash ) (например, busybox ash ) вы можете обнаружить, что его обработка ссылок файлового дескриптора и busybox ash документов обеспечивает превосходную альтернативу тому, что вы привыкли делать с <( замещение процесса ) ,

    Рассмотрим этот пример:

     exec "$((i=3))"<<R "$((o=4))"<<W 3<>/dev/fd/3 4<>/dev/fd/4 R W sed -u 's/.*/here I am./' <&"$o" >&"$i" & echo "hey...sed?" >&"$o" head -n1 <&"$i" 

    Поскольку dash и производные обратно здесь – документы с анонимными трубами, а не (как это делают большинство других оболочек) с обычными файлами, а также потому, что ссылки /dev/fd/[num] в Linux-системах предоставляют косвенный способ ссылаться на файл-дескриптор (даже если на файловую систему нельзя ссылаться, например, на анонимные трубы), приведенная выше последовательность демонстрирует очень простой способ настройки того, что некоторые оболочки могут называть копроцессом . Например, в busybox ash или dash в Linux-системе (я не буду ручаться за других) выше будет напечатать:

     here I am. 

    … и будет продолжать делать это до тех пор, пока оболочка не завершит свои файловые дескрипторы $i и $o . Для предотвращения проблем с буферами используется опциональный коммутатор GNU -Unuffer, но даже без него входной сигнал фонового процесса может быть отфильтрован и conv=sync в блоках из \0NUL байтов w / dd в конвейере, если это необходимо.

    Вот как я обычно использую приведенное выше с sed в интерактивной оболочке:

     : & SEDD=$$$! sed -un "/^$SEDD$/!H;//!d;s///;x;/\n/!q;s///;s/%/&&/g;l" <&"$o" >&"$i" & 

    … который sed который будет считывать и хранить входные данные до тех пор, пока не встретит уникальный разделитель, и в это время он удвоит любое количество % в своем старом буфере H и распечатает мой анонимный канал exec – искомая строка в одной строке – или, на нескольких строках, если результат больше 80 символов. Это последнее – для GNU sed – можно обрабатывать w / sed -l0 который является коммутатором, который будет инструктировать sed никогда не обертывать строки на \ , иначе:

     fmt= while IFS= read -rr <&"$i" case $r in (*$) ! fmt=$fmt$r ;;esac do fmt=$fmt${r%?} done 

    Во всяком случае, я строю свой буфер как:

     echo something at sed >&"$o" printf '%s\n' more '\lines%' at sed "$SEDD" >&"$o" 

    Затем я тяну его, как …

     IFS= read -r fmt <&"$i" 

    Вот как выглядит содержимое $fmt :

     printf %s\\n "$fmt" something at sed\nmore\n\\lines%%\nat\nsed$ 

    sed также сделает восьмеричные экранизации C-стиля для непечатаемых символов.

    Поэтому я могу использовать его, как …

     printf "%d\n${fmt%$}\n" 1 2 3 

    … который печатает …

     1 something at sed more \lines% at sed 2 something at sed more \lines% at sed 3 something at sed more \lines% at sed 

    И я могу убить sed и выпустить трубы по мере необходимости, как …

     printf %s\\n "$SEDD" "$SEDD" >&"$o" exec "$i">&- "$o">&- 

    Это то, что вы можете делать, когда вы держитесь за fd, а не используете его только один раз. Вы можете поддерживать обратную связь до тех пор, пока вам может понадобиться, – и она более безопасна, чем именованный канал, потому что ядро ​​не предлагает эти ссылки никому, кроме процесса, которому они принадлежат (ваша оболочка) , тогда как именованный канал может быть найден (и постучал / украден) в файловой системе любым процессом с разрешениями на его файл ссылки.

    Чтобы делать подобные вещи в оболочке, которая выполняет замещение процесса, вы, вероятно, можете сделать …

     eval "exec [num]<>"<(:) 

    … но я никогда не пробовал.

    Сделайте это так:

     i=1 my_command | while read line; do echo $line eval VAL$i="$line" i=$((i+1)) done 

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

    EDIT: Поскольку переменные VALx заданы в подоболочке выше, необходима модификация:

     eval `i=1 my_command | while read line; do # echo $line echo "VAL$i=\"$line\"" i=$((i+1)) done` 

    Если вам действительно нужна линия echo $line вам понадобятся некоторые изменения.

    Я реализовал это с помощью этого документа:

      i=1 while read -r line; do eval VAL$i=\$line i=$((i+1)) done <<EOF $(my_command) EOF 

    Работает отлично.

    Обновление: объединенные отзывы от Gilles и mikeserv.

    Linux и Unix - лучшая ОС в мире.