Bash: Como ler uma linha de cada vez da saída de um comando?

41

Eu estou tentando ler a saída de um comando no bash usando while loop .

while read -r line
do
    echo "$line"
done <<< $(find . -type f)

A saída que recebi

ranveer@ranveer:~/tmp$ bash test.sh
./test.py ./test1.py ./out1 ./test.sh ./out ./out2 ./hello
ranveer@ranveer:~/tmp$ 

Depois disso eu tentei

$(find . -type f) | 
while read -r line
do
    echo "$line"
done 

mas gerou um erro test.sh: line 5: ./test.py: Permission denied .

Então, como leio linha por linha porque acho que atualmente está sugando toda a linha de uma só vez.

Resultado exigido:

./test.py
./test1.py
./out1
./test.sh
./out
./out2
./hello
    
por RanRag 16.10.2012 / 22:35

2 respostas

42

Há um erro, você precisa de < <(command) não <<<$(command)

< <( ) é uma Substituição de processo , $() é um substituição de comandos e <<< é uma string aqui .

    
por 16.10.2012 / 22:36
13

Observe que não há nada que impeça os nomes de arquivo de conter caracteres de nova linha. A maneira canônica de executar um comando para cada arquivo encontrado por find é.

find . -type f -exec cmd {} \;

E se você quiser que as coisas sejam feitas no bash:

find . -type f -exec bash -c '
  for file do
    something with "$file"
  done' bash {} +

Além disso, a maneira canônica de chamar o comando "read" nos scripts (se você não quiser fazer processamento extra na entrada) é:

IFS= read -r var

-r é para parar read de tratar caracteres de barra invertida especialmente (como um caractere de escape para separadores e nova linha) E IFS = para definir a lista de separadores para a cadeia vazia por read (caso contrário, se houver espaço em branco caractere estava nessa lista, eles seriam retirados do início e do final da entrada).

Usar loops em shells geralmente é uma má idéia (não como as coisas são feitas em shells, onde várias ferramentas funcionam coletivamente e simultaneamente em uma tarefa, em vez de executar uma ou mais ferramentas centenas de vezes em sequência).

    
por 16.10.2012 / 23:32