Разделение и зацикливание на выводе команды в реальном времени в Bash

#bash #shell

#bash #оболочка

Вопрос:

Я архивирую и использую split для создания нескольких частей, одновременно печатая выходные файлы (из split STDERR, которые я перенаправляю на стандартный вывод). Однако цикл над выходными данными не выполняется до тех пор, пока команда не вернется.

Есть ли в любом случае возможность активного разделения на стандартный вывод команды перед ее возвратом?

Ниже приведено то, что у меня есть в настоящее время, но он выводит список имен файлов только после возврата команды:

 export IFS=$'n'
for line in `data_producing_command | split -d -b $CHUNK_SIZE --verbose - $ARCHIVE_PREFIX 2>amp;1`; do
    FILENAME=`echo $line | awk '{ print $3 }'`
    echo "    - $FILENAME"
done
  

Комментарии:

1. Можете ли вы добавить текущий и ожидаемый результат? или, возможно, перефразируйте вопрос. Не понял, чего вы пытаетесь достичь.

2. Я спрашиваю, как активно разделять и печатать вывод команды, которая постоянно печатается в стандартный вывод. То, что я предоставляю, печатается только после завершения команды.

Ответ №1:

Попробуйте это:

 data_producing_command | split -d -b $CHUNK_SIZE --verbose - $ARCHIVE_PREFIX 2>amp;1 | while read -r line
do
    FILENAME=`echo $line | awk '{ print $3 }'`
    echo "    - $FILENAME"
done
  

Однако обратите внимание, что любые переменные, установленные в while цикле, не будут сохранять свои значения после цикла ( while цикл выполняется в подоболочке).

Комментарии:

1. Вы также можете передавать for : cat aFileToMakeStdout |for n in $(cat -) ; do echo $n ;done

2. @Leon Никаких изменений. В чем разница в функциях между зацикливанием на неявном выполнении и вызовом «read»?

3. @LinuxDisciple Это, по сути, то, что у меня было.

4. @DustinOprea Во время замены команды (при вызове команды между обратными метками или лучше с использованием рекомендуемого синтаксиса $(command) ) команда должна завершиться до того, как ее полный вывод будет развернут в сценарий оболочки. В канале элементы канала могут выполняться параллельно.

5. @Leon Хотя это исключительный момент, я попробовал оба (а затем попробовал ваш метод). Вот почему странно, что это не сработало.

Ответ №2:

Нет причин для цикла for или чтения или echo. Просто передайте поток в awk:

 ... | split -d -b $CHUNK_SIZE --verbose - test 2>amp;1 |
 awk '{printf "    - %sn", $3 }'
  

Вы увидите некоторую задержку буферизации, но если ваша система не работает очень медленно или вы не очень проницательны, вы вряд ли это заметите.

Ответ №3:

Для выполнения замены команды требуется 1, прежде чем for цикл может начаться.

 for item in $(command which produces items); do ...
  

в то время как a while read -r может начать потреблять выходные данные, как только будет создана первая строка (или, что более реалистично, как только выходной буфер заполнится):

 command which produces items |
while read -r item; do ...
  

1 Ну, я полагаю, это не обязательно, с точки зрения дизайна, но именно так это работает в настоящее время.

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

 command which produces items |
awk '{ print "    - " $3 }'
  

Конечно, с достаточно недавним GNU Coreutils split вы могли бы просто сделать

 split --filter='printf "   - %sn" "$FILE"'; cat >"$FILE" ... options