2013-08-30 3 views
4

У меня есть следующий код, который считывает несколько команд, печатает их и выводит их вывод.Команда prepend, stdout и stderr

while read line ; do 
    echo "C:$line" 
    echo "O:$(${line} 2>&1 | perl -pe 's,\n,\\n,'g)\n" 
done << EOF 
g++-4.8 -O2 -Wall -Wextra -pedantic -pthread main.cpp 
./a.out 
EOF 

Выход:

C:g++-4.8 -O2 -Wall -Wextra -pedantic -pthread main.cpp 
O:main.cpp: In function ‘int main(int, char**)’:\nmain.cpp:3:9: warning: unused variable ‘unused’ [-Wunused-variable]\n  int unused;\n   ^\n\n 
C:./a.out 
O:*** glibc detected *** ./a.out: munmap_chunk(): invalid pointer: 0x00007fff3bd01a5c ***\n======= Backtrace: =========\n/lib/x86_64-linux-gnu/libc.so.6(+0x7eb96)[0x7f6960e1ab96]\n./a.out[0x400502]\n/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed)[0x7f6960dbd76d]\n./a.out[0x400535]\n======= Memory map: ========\n\n 

Я хотел бы различать стандартный вывод и стандартный поток ошибок и использовать 'E:' префикс для STDERR. Также я хотел бы напечатать код выхода каждой командной строки.

Как я могу это достичь?

+0

Просто предупреждение: имейте в виду, что-то вроде 'a.out«строковым»' в документ-здесь может привести ваш код ломаться, если 'a.out' ожидает один аргумент. Здесь 'a.out' получит 2:' 'a' и' string ''. – chepner

ответ

9
#!/bin/bash 

# Add a prefix to each line of stdin. 
prefix() { 
    local line 
    while read line; do printf '%s%s\n' "$1" "$line"; done 
} 

# Execute each command. Notice the `(' and `)' surrounding the loop body, which starts 
# a sub-shell for each iteration. Running in a sub-shell lets us use `trap EXIT' to 
# cleanup. 
while read command; do (
    # Create FIFOs for the command's stdout and stderr. 
    stdout=$(mktemp -u) 
    stderr=$(mktemp -u) 
    mkfifo "$stdout" "$stderr" 

    # Delete the FIFOs when this iteration of the loop finishes. Use `trap' to ensure 
    # cleanup happens whether we finish normally or are signalled. 
    trap 'rm -f "$stdout" "$stderr"' EXIT 

    # Read from the FIFOs in the background, adding the desired prefixes. 
    prefix 'O:' < "$stdout" >&1 & 
    prefix 'E:' < "$stderr" >&2 & 

    # Now execute the command, sending its stdout and stderr to the FIFOs. 
    echo "C:$command" 
    eval "$command" 1> "$stdout" 2> "$stderr" 
    exitcode=$? 

    # Wait for the `prefix' processes to finish, then print the exit code. 
    wait 
    echo "R:$exitcode" 
    exit $exitcode 
) done 
+0

Спасибо. Это очень здорово! – StackedCrooked

1

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

Я думаю, что главным виновником этой проблемы является то, что оператор | не позволяет указать поток, аналогичный тому, как перенаправление действует, например. 2>.

Решение, к которому я пришел, состоит в том, чтобы объединить выходные данные ряда подоболочек вместе с внутренней обработкой stdout и затем перенаправить ее на альтернативный временный поток 3.

Следующая подоболочка перенаправляет stderr в stdin снова и повторяет активность внутренней оболочки (хотя и с префиксом «E:», а не «О:»). Он перенаправляет вывод здесь на stdout, хотя >&2 может быть удален, если вы хотите, чтобы иметь все в stdin (но я думаю, что сохранение этих потоков отдельно является преимуществом).

Наружная оболочка снова присоединяется &3 поток с stdin.

Поскольку две внутренние оболочки обрабатывать stdin и stdout отдельно (из-за альтернативный «O:» и «E:» префиксы) необходимо выполнить команду perl в два раза, так что я завернут его в fold функции чтобы все было аккуратно, и здесь также добавляются различные префиксы.

Вы могли бы покончить с sed и включают в себя, что в perl регулярных выражений, а также быть в курсе, что \\n вводится в конце каждой строки вашей команды perl. На самом деле мое личное мнение состоит в том, что линейная сворачивание, введенное этой командой, теперь не должно быть необходимым, но я сохранил ее для верности с вашей оригинальной проблемой.

function fold { 
    perl -pe 's,\n,\\n,'g | sed 's/^\(.*\)$/'${1}':\1\n/' 
} 

while read line ; do 
    echo "C:$line" 
    (
     (
      ${line} | fold O >&3 
     ) 2>&1 | fold E >&2 
    ) 3>&1 
done 
+0

Спасибо. Я думаю, что это умная идея использовать подоболочки таким образом. – StackedCrooked

Смежные вопросы