Lendo vários arquivos uma linha por vez no bash

4

Eu sei o modo básico de ler de um comando no bash:

cal | while IFS= read -r line ; do 
    echo X${line}X
done

Mas e se eu quiser ler uma linha de vários arquivos / comandos em um loop? Eu tentei pipes nomeados, mas eles só cuspiam uma linha.

$ cal > myfifo &
$ IFS= read -r line < myfifo
[cal exits]
$ echo $line
   February 2015      

Então, o que eu realmente quero é algo como:

while [all pipes are not done]; do
    IFS=
    read line1 < pipe1    # reading one line from this one
    read line2 < pipe2    # and one line from this one
    read line3 < pipe3    # and one line from this one
    print things with $line1 $line2 and $line3
done

Imagem geral O que estou tentando fazer é processar três meses diferentes de cal para fazer um pouco de colorização para uso com Conky. É um pouco de barba de iaque, honestamente, então se tornou acadêmica e uma 'experiência de aprendizado' neste momento.

    
por Greg Bell 03.02.2015 / 00:46

4 respostas

6

paste seria a maneira mais fácil de fazer isso. Mas, com descritores de arquivos bash e substituições de processos:

exec 3< <(cal 1 2015)
exec 4< <(cal 2 2015)
exec 5< <(cal 3 2015)

while IFS= read -r -u3 line1; do
    IFS= read -r -u4 line2
    IFS= read -r -u5 line3
    printf "%-25s%-25s%-25s\n" "$line1" "$line2" "$line3"
done

exec 3<&-
exec 4<&-
exec 5<&-
    January 2015             February 2015             March 2015          
Su Mo Tu We Th Fr Sa     Su Mo Tu We Th Fr Sa     Su Mo Tu We Th Fr Sa     
             1  2  3      1  2  3  4  5  6  7      1  2  3  4  5  6  7     
 4  5  6  7  8  9 10      8  9 10 11 12 13 14      8  9 10 11 12 13 14     
11 12 13 14 15 16 17     15 16 17 18 19 20 21     15 16 17 18 19 20 21     
18 19 20 21 22 23 24     22 23 24 25 26 27 28     22 23 24 25 26 27 28     
25 26 27 28 29 30 31                              29 30 31                 
    
por 03.02.2015 / 02:49
2

Você pode usar paste para combinar a saída e ler as linhas:

paste -d $'\n' <(foo) <(bar) <(cat baz) | while IFS= read -r line1
do
    IFS= read -r line2 || break
    IFS= read -r line3 || break
    # ..
done
    
por 03.02.2015 / 00:54
0

Este é o tipo de estrutura que você precisará.

Você estava na linha certa com seu FIFO, mas a razão cal que saiu quando você leu uma linha é que read -r line < myfifo abriu o FIFO, leu uma linha e fechou-a novamente. Fechar um tubo envia um sinal para o outro lado, indicando que não é possível escrever mais (ler). Então cal saiu.

# Create the FIFOs
mknod pipe1 p
mknod pipe2 p
mknod pipe3 p

# Start off the commands
command1 >pipe1 &
command2 >pipe2 &
command3 >pipe3 &

# Attach file descriptors to the other side of the FIFOs
exec 11<pipe1 12<pipe2 13<pipe3

# Loop
IS_MORE=0
while [[ 0 eq $IS_MORE ]]
do
    IS_MORE=1
    read LINE1 <&11 && IS_MORE=0
    read LINE2 <&12 && IS_MORE=0
    read LINE3 <&13 && IS_MORE=0
    # ...
done

# Close descriptors and remove the FIFOs
exec 11<&- 12<&- 13<&-
rm -f pipe1 pipe2 pipe3

# Clean up the dead processes (zombies)
wait

# ...
    
por 03.02.2015 / 01:00
0
while \
    IFS= read -r -u3 line1;do
    IFS= read -r -u4 line2
    IFS= read -r -u5 line3
    printf "%-25s%-25s%-25s\n" "$line1" "$line2" "$line3"
done 3< <(cal -h 1 2018) 4< <(cal -h 2 2018) 5< <(cal -h 3 2018)

Ou talvez o seguinte, se os 3 meses forem os seguintes:

while IFS= read -r;do
    printf "%s\n" "${REPLY}"
done < <(cal -A 1 -B 1 -h 8 2018)
    
por 10.01.2018 / 20:06

Tags