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

이는 두 가지 모드로 작동할 수 있습니다.

$ ./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}) \
...

관련 정보