¿Cómo aplicar el comando de shell a cada línea de una salida de comando?

192

Supongamos que tengo un resultado de un comando (como ls -1):

a
b
c
d
e
...

Quiero aplicar un comando (por ejemplo echo) a cada uno, a su vez. P.ej

echo a
echo b
echo c
echo d
echo e
...

¿Cuál es la forma más fácil de hacer eso en bash?

Alex Budovski
fuente
3
ls -1Puede ser un ejemplo aquí, pero es importante recordar que no es bueno analizar la salida de ls. Ver: mywiki.wooledge.org/ParsingLs
codeforester

Respuestas:

217

Probablemente sea más fácil de usar xargs. En tu caso:

ls -1 | xargs -L1 echo

La -Lbandera asegura que la entrada se lea correctamente. De la página del manual de xargs:

-L number
    Call utility for every number non-empty lines read. 
    A line ending with a space continues to the next non-empty line. [...]
Michael Mrozek
fuente
24
lsLo hace automáticamente -1en una tubería.
Pausado hasta nuevo aviso.
55
@Dennis, no lo parece: ls | xargs -L2 echoy ls -1 | xargs -L2 echoda dos salidas diferentes. El primero está todo en una línea.
Alex Budovski
2
@Alex: obtengo la misma salida.
Pausado hasta nuevo aviso.
66
xargssolo puede ejecutar archivos ejecutables, no funciones de shell o comandos integrados de shell. Para el primero, la mejor solución es probablemente la que está readen un bucle.
pabouk
12
Desearía que esta respuesta incluyera una explicación de para qué -L1sirve.
Wyck
164

Puede usar una operación de anteponer básica en cada línea:

ls -1 | while read line ; do echo $line ; done

O puede canalizar la salida a sed para operaciones más complejas:

ls -1 | sed 's/^\(.*\)$/echo \1/'
Trey Hunner
fuente
1
El comando sed no parece funcionar: sh: cho: not found a sh: cho: not foundparece que está tomando el eeco como un comando sed o algo así.
Alex Budovski
+1 para el whilebucle. cmd1 | while read line; do cmd2 $line; done. O while read line; do cmd2 $line; done < <(cmd1)que no crea una subshell. Esta es la versión simplificada de su sedcomando:sed 's/.*/echo &/'
pausa hasta nuevo aviso.
1
@ Alex: cambie las comillas dobles a comillas simples.
Pausado hasta nuevo aviso.
55
Cite el "$line"bucle while para evitar la división de palabras.
ignis
3
Intenta usar read -r linepara evitar readjugar con los personajes escapados. Por ejemplo , echo '"a \"nested\" quote"' | while read line; do echo "$line"; doneda "a "nested" quote", que ha perdido su fuga. Si lo hacemos echo '"a \"nested\" quote"' | while read -r line; do echo "$line"; done, obtenemos "a \"nested\" quote"lo esperado. Ver wiki.bash-hackers.org/commands/builtin/read
Warbo
9

Puede usar un bucle for :

para archivo en *; hacer
   echo "$ archivo"
hecho

Tenga en cuenta que si el comando en cuestión acepta múltiples argumentos, el uso de xargs es casi siempre más eficiente, ya que solo tiene que generar la utilidad en cuestión una vez en lugar de varias veces.

Michael Aaron Safyan
fuente
1
Vale la pena describir el uso adecuado / seguro de xargs, es decir. printf '%s\0' * | xargs -0 ...- de lo contrario, es bastante inseguro con nombres de archivo con espacios en blanco, comillas, etc.
Charles Duffy
8

En realidad, puede usar sed para hacerlo, siempre que sea GNU sed.

... | sed 's/match/command \0/e'

Cómo funciona:

  1. Sustituir partido con comando partido
  2. En sustitución ejecutar comando
  3. Reemplace la línea sustituida con la salida del comando.
Łukasz Daniluk
fuente
Fantástico. Olvidé poner el comando e al final, pero lo hice después de ver su respuesta y funcionó. Estaba tratando de agregar una identificación aleatoria entre 1000 y 15000, cuando SED coincide con una línea. cat /logs/lfa/Modified.trace.log.20150904.pw | sed -r 's/^(.*)(\|006\|00032\|)(.*)$/echo "\1\2\3 - ID `shuf -i 999-14999 -n 1`"/e'
sgsi
2

xargs falla con barras invertidas, comillas. Tiene que ser algo como

ls -1 |tr \\n \\0 |xargs -0 -iTHIS echo "THIS is a file."

Opción xargs -0:

-0, --null
          Input  items are terminated by a null character instead of by whitespace, and the quotes and backslash are
          not special (every character is taken literally).  Disables the end of file string, which is treated  like
          any  other argument.  Useful when input items might contain white space, quote marks, or backslashes.  The
          GNU find -print0 option produces input suitable for this mode.

ls -1termina los elementos con caracteres de nueva línea, por lo que los trtraduce en caracteres nulos.

Este enfoque es aproximadamente 50 veces más lento que iterar manualmente con for ...(ver la respuesta de Michael Aaron Safyan ) (3.55s vs. 0.066s). Pero para otros comandos de entrada como localizar, buscar, leer desde un archivo ( tr \\n \\0 <file) o similar, debe trabajar de xargsesta manera.

phil294
fuente
1

Mejor resultado para mí:

ls -1 | xargs -L1 -d "\n" CMD
Andrej Pandovich
fuente
Mejor, pero no perfecto. find . -mindepth 1 -maxdepth 1 -print0 | xargs -0 commandmanejará casos donde la salida de ls -1es ambigua; use en -printf '%P\0'lugar de -print0si no quiere una ventaja ./en cada uno.
Charles Duffy
1

me gusta usar gawk para ejecutar múltiples comandos en una lista, por ejemplo

ls -l | gawk '{system("/path/to/cmd.sh "$1)}'

Sin embargo, el escape de los personajes escapables puede ponerse un poco peludo.

Chris
fuente