Bash: ¿Cómo leer una línea a la vez desde la salida de un comando?

49

Estoy tratando de leer la salida de un comando en bash usando a while loop.

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

La salida que obtuve

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

Después de esto intenté

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

pero genera un error test.sh: line 5: ./test.py: Permission denied.

Entonces, ¿cómo lo leo línea por línea porque creo que actualmente está sorbiendo toda la línea a la vez?

Salida requerida:

./test.py
./test1.py
./out1
./test.sh
./out
./out2
./hello
RanRag
fuente
3
Sugiero leer Bash FAQ 01 - mucha información útil y consejos sobre trampas para evitar.
jw013
Para la while readparte, vea Comprender IFS y las preguntas vinculadas allí.
Gilles 'SO- deja de ser malvado'
Para usar find, vea ¿Cómo puedo usar dos comandos bash en -exec del comando find? o Ejecutar la función definida por el usuario en una llamada find -exec (de la cual esta pregunta es principalmente un duplicado).
Gilles 'SO- deja de ser malvado'

Respuestas:

54

Hay un error, es necesario < <(command)no<<<$(command)

< <( )es una sustitución de proceso , $()es una sustitución de comando y <<<es una cadena aquí .

Gilles Quenot
fuente
2
@RanRag ¡Deja de intentar arreglarlo $( )todo! Esa es la sintaxis para la sustitución de comandos , que es solo una forma de usar la salida de comandos. Las tuberías y la sustitución de procesos y las cadenas aquí son otras, y todas tienen una sintaxis diferente, naturalmente. No debería analizar los nombres de archivo de todos modos a menos que realmente sepa lo que está haciendo.
jw013
Gracias funcionó leerá más sobre Process Substitution.
RanRag
@ jw013: Soy un principiante. En el futuro mantendré tu sugerencia en mi mente.
RanRag
13

Tenga en cuenta que no hay nada que impida que los nombres de archivo contengan caracteres de nueva línea. La forma canónica de ejecutar un comando para cada archivo encontrado por find es.

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

Y si quieres que las cosas se hagan en bash:

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

Además, la forma canónica de llamar al comando "leer" en los scripts (si no desea que realice un procesamiento adicional en la entrada) es:

IFS= read -r var

-res para dejar readde tratar los caracteres de barra invertida especialmente (como un carácter de escape para separadores y nueva línea), e IFS = para establecer la lista de separadores en la cadena vacía read(de lo contrario, si algún carácter de espacio en blanco estuviera en esa lista, se eliminarían del principio y fin de la entrada).

Por lo general, usar bucles en shells es una mala idea (no cómo se hacen las cosas en shells donde haces que varias herramientas funcionen de manera colectiva y simultánea para una tarea en lugar de ejecutar una o más herramientas cientos de veces en secuencia).

Stéphane Chazelas
fuente