fifosを使用するスクリプトは、標準入力で処理しても出力を生成しません。

fifosを使用するスクリプトは、標準入力で処理しても出力を生成しません。

名前付きパイプを使用して入力データを並列に処理し、結果を再度貼り付けようとします。標準入力(下)から入力を受け取る可能性を追加するまで、いくつかの作業がありました。この回答)。

ここでは、列のみを選択して貼り付ける前に追加の処理を行わない簡単な例を使用して問題を報告しますが、実際のスクリプトはそれ以上を行います。

データ例:

$ cat data.txt
A1  A2  A3  A4  A5
B1  B2  B3  B4  B5
C1  C2  C3  C4  C5
D1  D2  D3  D4  D5
E1  E2  E3  E4  E5
F1  F2  F3  F4  F5
G1  G2  G3  G4  G5
H1  H2  H3  H4  H5
I1  I2  I3  I4  I5
J1  J2  J3  J4  J5
K1  K2  K3  K4  K5
L1  L2  L3  L4  L5

一般ファイルを使用するスクリプト:

$ cat test_files.sh
#!/bin/bash

get_1()
{
    cut -f1 - > ${1}
}

get_3()
{
    cut -f3 - > ${1}
}

get_5()
{
    cut -f5 - > ${1}
}


setup()
{
    workdir=$(mktemp -d)
    col1="${workdir}/col1.txt"
    col3="${workdir}/col3.txt"
    col5="${workdir}/col5.txt"
}

setup

cleanup()
{
    rm -rf ${workdir}
}

if [ $# -ge 1 -a -f "${1}" ]
then
    cat ${1} \
        | tee >(get_1 ${col1}) \
        | tee >(get_3 ${col3}) \
        | get_5 ${col5} \
        || { echo "cat failed" && cleanup && exit 1; }
else
    cat - \
        | tee >(get_1 ${col1}) \
        | tee >(get_3 ${col3}) \
        | get_5 ${col5} \
        || { echo "cat failed" && cleanup && exit 1; }
fi

paste ${col1} ${col3} ${col5}
cleanup
exit 0

これは両方のモードでうまく機能します。

$ ./test_files.sh data.txt
A1  A3  A5
B1  B3  B5
C1  C3  C5
D1  D3  D5
E1  E3  E5
F1  F3  F5
G1  G3  G5
H1  H3  H5
I1  I3  I5
J1  J3  J5
K1  K3  K5
L1  L3  L5
$ cat data.txt | ./test_files.sh
A1  A3  A5
B1  B3  B5
C1  C3  C5
D1  D3  D5
E1  E3  E5
F1  F3  F5
G1  G3  G5
H1  H3  H5
I1  I3  I5
J1  J3  J5
K1  K3  K5
L1  L3  L5

以下はfifosを使用するバージョンです。私はバックグラウンドで熱抽出を行い、fifosから貼り付けます。

$ cat test_fifos.sh
#!/bin/bash

get_1()
{
    cut -f1 - > ${1}
}

get_3()
{
    cut -f3 - > ${1}
}

get_5()
{
    cut -f5 - > ${1}
}


setup()
{
    workdir=$(mktemp -d)
    col1="${workdir}/col1.txt"
    mkfifo ${col1}
    col3="${workdir}/col3.txt"
    mkfifo ${col3}
    col5="${workdir}/col5.txt"
    mkfifo ${col5}
}

setup

cleanup()
{
    rm -rf ${workdir}
}

if [ $# -ge 1 -a -f "${1}" ]
then
    cat ${1} \
        | tee >(get_1 ${col1}) \
        | tee >(get_3 ${col3}) \
        | get_5 ${col5} \
        || { echo "cat failed" && cleanup && exit 1; } &
else
    cat - \
        | tee >(get_1 ${col1}) \
        | tee >(get_3 ${col3}) \
        | get_5 ${col5} \
        || { echo "cat failed" && cleanup && exit 1; } &
fi

paste ${col1} ${col3} ${col5}
cleanup
exit 0

入力ファイルを引数として使用すると機能します。

$ ./test_fifos.sh data.txt
A1  A3  A5
B1  B3  B5
C1  C3  C5
D1  D3  D5
E1  E3  E5
F1  F3  F5
G1  G3  G5
H1  H3  H5
I1  I3  I5
J1  J3  J5
K1  K3  K5
L1  L3  L5

ただし、stdinからデータをインポートするときに出力はありません。

$ cat data.txt | ./test_fifos.sh
$ # Nothing here, no error message

最小限の例を生成するために、いくつかの実験を実行しながら、潜在的に誤ったコードを処理することが問題の一部であるように見えました。以下は、fifosを使用してエラーを処理しないバージョンです。

$ cat test_fifos_noerr.sh
#!/bin/bash

get_1()
{
    cut -f1 - > ${1}
}

get_3()
{
    cut -f3 - > ${1}
}

get_5()
{
    cut -f5 - > ${1}
}


setup()
{
    workdir=$(mktemp -d)
    col1="${workdir}/col1.txt"
    mkfifo ${col1}
    col3="${workdir}/col3.txt"
    mkfifo ${col3}
    col5="${workdir}/col5.txt"
    mkfifo ${col5}
}

setup

cleanup()
{
    rm -rf ${workdir}
}

if [ $# -ge 1 -a -f "${1}" ]
then
    cat ${1} \
        | tee >(get_1 ${col1}) \
        | tee >(get_3 ${col3}) \
        | get_5 ${col5} &
else
    cat - \
        | tee >(get_1 ${col1}) \
        | tee >(get_3 ${col3}) \
        | get_5 ${col5} &
fi

paste ${col1} ${col3} ${col5}
cleanup
exit 0

これは2つのモードで動作できます。

$ ./test_fifos_noerr.sh data.txt
A1  A3  A5
B1  B3  B5
C1  C3  C5
D1  D3  D5
E1  E3  E5
F1  F3  F5
G1  G3  G5
H1  H3  H5
I1  I3  I5
J1  J3  J5
K1  K3  K5
L1  L3  L5
$ cat data.txt | ./test_fifos_noerr.sh
A1  A3  A5
B1  B3  B5
C1  C3  C5
D1  D3  D5
E1  E3  E5
F1  F3  F5
G1  G3  G5
H1  H3  H5
I1  I3  I5
J1  J3  J5
K1  K3  K5
L1  L3  L5

stdinからデータを取得してfifosを使用したときに発生する可能性のあるエラーを処理するときに出力がないのはなぜですか?


編集:いくつかのデバッグ

失敗したスクリプトにいくつかのデバッグ出力を追加しました。

$ cat test_fifos.sh
#!/bin/bash

get_1()
{
    >&2 echo "get_1"
    cut -f1 - > ${1}
}

get_3()
{
    >&2 echo "get_3"
    cut -f3 - > ${1}
}

get_5()
{
    >&2 echo "get_5"
    cut -f5 - > ${1}
}


setup()
{
    workdir=$(mktemp -d)
    col1="${workdir}/col1.txt"
    mkfifo ${col1}
    col3="${workdir}/col3.txt"
    mkfifo ${col3}
    col5="${workdir}/col5.txt"
    mkfifo ${col5}
}

setup

cleanup()
{
    >&2 echo "cleanup"
    rm -rf ${workdir}
}

if [ $# -ge 1 -a -f "${1}" ]
then
    >&2 echo "then"
    cat ${1} \
        | tee >(get_1 ${col1}) \
        | tee >(get_3 ${col3}) \
        | get_5 ${col5} \
        || { >&2 echo "cat failed" && cleanup && exit 1; } &
else
    >&2 echo "else"
    cat - \
        | tee >(get_1 ${col1}) \
        | tee >(get_3 ${col3}) \
        | get_5 ${col5} \
        || { >&2 echo "cat failed" && cleanup && exit 1; } &
fi

>&2 echo "before paste"
paste ${col1} ${col3} ${col5}
>&2 echo "after paste"
cleanup
exit 0

stdinからデータを読み取ると、次のようになります。

$ cat data.txt | ./test_fifos.sh
else
before paste
get_3
get_5
get_1
after paste
cleanup

したがって、これはelse分岐が実行されたことを意味します。

答え1

&バックグラウンドでコマンドを実行する際の問題は、シェルがコマンドへの標準入力を自動的に閉じるということです。したがって、cat -ファイルの終わりをすぐに読みます。たとえば、回避策としてファイル記述子0を3にコピーした後、次のようになります。

...
else    exec 3<&0
        cat - <&3 \
        | tee >(get_1 ${col1}) \
...

関連情報