linux - Cómo pasar la salida del comando como argumentos múltiples a otro comando
shell unix (4)
Además de la
buena respuesta de Chris Jester-Young
, diría que
xargs
también es una buena solución para estas situaciones:
grep ... `ls -lad ... | awk ''{ print $9 }''` | xargs kill -9
lo haremos. Todos juntos:
grep -hP ''^/d+$'' `ls -lad /dir/*/pid | grep -P ''/dir//d+/pid'' | awk ''{ print $9 }''` | xargs kill -9
Quiero pasar cada salida de un comando como argumento múltiple a un segundo comando, por ejemplo:
grep "pattern" input
devoluciones:
file1
file2
file3
y quiero copiar estos resultados, por ejemplo:
cp file1 file1.bac
cp file2 file2.bac
cp file3 file3.bac
¿Cómo puedo hacer eso de una vez? Algo como:
grep "pattern" input | cp $1 $1.bac
Para completar, también mencionaré la sustitución de comandos y explicaré por qué esto no se recomienda:
cp $(grep -l "pattern" input) directory/
(La sintaxis de backtick
cp `grep -l "pattern" input` directory/
es más o menos equivalente, pero es obsoleta y difícil de manejar; no la use).
Esto fallará si la salida de
grep
produce un nombre de archivo que contiene espacios en blanco o un metacarácter de shell.
Por supuesto, está bien usar esto si sabe exactamente qué nombres de archivo puede producir el
grep
, y ha verificado que ninguno de ellos es problemático.
Pero para un guión de producción,
no use esto.
De todos modos, para el escenario del OP, donde debe referirse a cada coincidencia individualmente y agregarle una extensión, los
xargs
o las alternativas de
while read
son superiores de todos modos.
En el peor de los casos (es decir, nombres de archivo problemáticos o no especificados), pase las coincidencias a una subshell a través de
xargs
:
grep -l "pattern" input |
xargs -r sh -c ''for f; do cp "$f" "$f.bac"; done'' _
... donde obviamente el script dentro del ciclo
for
podría ser arbitrariamente complejo.
En el caso ideal, el comando que desea ejecutar es lo suficientemente simple (o versátil) como para simplemente pasarle una lista arbitrariamente larga de nombres de archivo.
Por ejemplo, GNU
cp
tiene una opción
-t
para facilitar este uso de
xargs
(la opción
-t
permite colocar el directorio de destino
primero
en la línea de comando, para que pueda colocar tantos archivos como desee al final del comando ):
grep -l "pattern" input | xargs cp -t destdir
que se expandirá en
cp -t destdir file1 file2 file3 file4 ...
para tantas coincidencias como
xargs
puedan caber en la línea de comando de
cp
, repite tantas veces como sea necesario para pasar todos los archivos a
cp
.
(Desafortunadamente, esto no coincide con el escenario del OP; si necesita cambiar el nombre de cada archivo durante la copia, debe pasar solo dos argumentos por invocación
cp
: el nombre del archivo de origen y el nombre del archivo de destino para copiarlo).
En otras palabras, si usa la sintaxis de sustitución de comandos y
grep
produce una lista
realmente
larga de coincidencias, corre el riesgo de toparse con los
ARG_MAX
y "Lista de argumentos demasiado larga";
pero
xargs
evitará esto específicamente copiando solo tantos argumentos como pueda pasar de manera segura a
cp
a la vez y ejecutando
cp
varias veces si es necesario.
Puede usar
$()
para interpolar la salida de un comando.
Entonces, podría usar
kill -9 $(grep -hP ''^/d+$'' $(ls -lad /dir/*/pid | grep -P ''/dir//d+/pid'' | awk ''{ print $9 }''))
si quisieras.
Puedes usar
xargs
:
grep ''pattern'' input | xargs -I% cp "%" "%.bac"