Estou tentando usar pipes nomeados para processar partes de dados de entrada em paralelo antes de colar os resultados juntos. Eu tinha algo funcionando até que eu adicionei a possibilidade de pegar a entrada de stdin (seguindo esta resposta ).
Aqui eu relato meu problema usando um exemplo simplificado que apenas seleciona colunas e não faz mais nenhum processamento antes de colar, mas o script real faz mais do que isso.
Exemplo de dados:
$ 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
Script usando arquivos normais:
$ 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
Este funciona bem nos dois modos:
$ ./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
Aqui está uma versão usando o fifos, onde executo a extração da coluna no fundo e cole dos 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
Funciona ao tomar um arquivo de entrada como argumento:
$ ./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
Mas ao obter os dados do stdin, não há saída:
$ cat data.txt | ./test_fifos.sh
$ # Nothing here, no error message
Fazendo algumas experiências para gerar um exemplo mínimo, percebi que o código para lidar com possíveis erros parecia ser parte do problema. Aqui está uma versão usando fifos e não tentando lidar com erros:
$ 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
Este funciona nos dois modos:
$ ./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
Por que não há saída quando ambos lidam com possíveis erros e usam fifos enquanto pegam os dados de stdin?
Editar: alguma depuração
Adicionei alguns resultados de depuração ao script com falha:
$ 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
Aqui está o que acontece quando os dados são lidos de stdin:
$ cat data.txt | ./test_fifos.sh
else
before paste
get_3
get_5
get_1
after paste
cleanup
Então, isso significa que a ramificação else
é executada.