4

いくつかのコマンドを読み取り、それらを印刷し、それらの出力を印刷する次のコードがあります。

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

stdout と stderr を区別し、stderr には 'E:' プレフィックスを使用したいと思います。また、各コマンドラインの終了コードを出力したいと思います。

どうすればこれを達成できますか?

4

2 に答える 2

10
#!/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
于 2013-08-30T16:00:57.157 に答える
1

これが私の提出物です。ジョンのものと似たようなことをしていると思いますが、行数が少ないようです。同様の問題があり、よりコンパクトなソリューションを決定したかったので、代替手段としてここに含めています。

この問題の主な原因は、パイプ|演算子では、リダイレクトと同様にストリームを指定できないことだと思います2>

ここで私がたどり着いた解決策は、多数のサブシェルの出力を連鎖させ、内側のサブシェルで処理stdoutし、それを別の一時ストリームにリダイレクトすることです3

次のサブシェルは再びリダイレクトstderrされ、内部シェルのアクティビティを繰り返します (ただし、 "O:"ではなく"E:"stdinをプレフィックスとして付けます)。ここで出力を再度リダイレクトしますが、すべてを入れたい場合は削除できます(ただし、これらのストリームを別々に保つことは利点だと思います)。stdout>&2stdin

外側のシェルは、再び&3ストリームにstdin.

2 つの内部シェルが別々に処理するstdinためstdout(代替の "O:" と "E:" プレフィックスのため)、コマンドを 2 回実行する必要があるため、整理するためにコマンドを関数にperlまとめました。これは次のとおりです。fold異なるプレフィックスも追加されます。

おそらく、を廃止して正規表現にsed含めることができます。また、コマンドによって各行の最後に a が導入されていることに注意してください。実際、私の個人的な意見では、このコマンドによって導入された行の折りたたみは今は必要ないはずですが、元の問題に忠実にするために保持しました。perl\\nperl

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
于 2014-09-20T12:08:18.870 に答える