¿Cómo fusionar y canalizar los resultados de dos comandos diferentes a un solo comando?


35

Quiero combinar la salida (unión) de dos comandos diferentes y canalizarlos a un solo comando.

Un ejemplo tonto:

Comandos Quiero fusionar la salida:

cat wordlist.txt
ls ~/folder/*

dentro:

wc -l

En este ejemplo, si wordlist.txt contiene 5 líneas y 3 archivos, quiero wc -ldevolver 8.

$cat wordlist.txt *[magical union thing]* ls ~/folder/* | wc -l
8

¿Cómo puedo hacer eso?

Respuestas:


56

Tu unión mágica es un punto y coma ... y llaves:

    { cat wordlist.txt ; ls ~/folder/* ; } | wc -l

Las llaves solo agrupan los comandos, de modo que el signo de tubería |afecta a la salida combinada.

También puede usar paréntesis ()alrededor de un grupo de comandos, que ejecutaría los comandos en una subshell. Esto tiene un conjunto sutil de diferencias con llaves, por ejemplo, intente lo siguiente:

    cd $HOME/Desktop ; (cd $HOME ; pwd) ; pwd
    cd $HOME/Desktop ; { cd $HOME ; pwd ; } ; pwd

Verá que todas las variables de entorno, incluido el directorio de trabajo actual, se restablecen después de salir del grupo de paréntesis, pero no después de salir del grupo de llaves.

En cuanto al punto y coma, las alternativas incluyen los signos &&y ||, que ejecutarán condicionalmente el segundo comando solo si el primero es exitoso o no, respectivamente, p. Ej.

    cd $HOME/project && make
    ls $HOME/project || echo "Directory not found."

1
Eso funciona! Ayúdame a aprender: ¿qué están haciendo exactamente las llaves aquí? ¿Qué otros poderes mágicos tienen?
David Oneill

@DavidOneill { list; }es un comando compuesto . From bash(1): list simplemente se ejecuta en el entorno actual de shell. La lista debe terminarse con una nueva línea o punto y coma. [..] El estado de retorno es el estado de salida de la lista.
Lekensteyn

He agregado un poco más de información a la respuesta. La guía definitiva para las secuencias de comandos de shell con bash es la página de manual de bash, escriba man bashdesde la línea de comandos y navegue por ella.
pablomme

si el ; en esos comandos no se && en caso de que el archivo wordlist.txt no exista?
Rinzwind,

Si wordlist.txtno existe cat, aparecerá un error en el error estándar, pero no en la salida estándar, por wc -llo que no contará ninguna línea desde él. Lo mismo vale para ~/folderno existir. Se podría agregar 2> /dev/nullentre cada uno de estos comandos y su punto y coma para evitar el ruido en el error estándar, pero aparte de ser feo, los mensajes de error son inofensivos con el fin de contar líneas.
pablomme

8

Como wcacepta una ruta de archivo como entrada, también puede usar la sustitución de procesos:

wc -l <(cat wordlist.txt; ls ~/folder/*)

Esto equivale aproximadamente a:

echo wordlist.txt > temp
ls ~/folder/* >> temp
wc -l temp

Tenga en cuenta que ls ~/folder/*también devuelve el contenido de los subdirectorios si los hay (debido a la expansión global). Si solo desea enumerar el contenido de ~/folder, simplemente use ls ~/folder.


El wc -lera sólo una compuesta por ejemplo, estoy realmente tuberías en algo más complicado que no tiene esta opción.
David Oneill

2
@DavidOneill Bueno, como catacepta un argumento de archivo, puede usar cat <(cat wordlist.txt; ls wordlist.txt) | wc -le incluso cat wordlist.txt <(ls wordlist.txt) | wc -l. Por supuesto, esto es muy feo, pero demuestra las infinitas posibilidades con las herramientas de línea de comandos.
Lekensteyn

1
@DavidOneill Correcto, lo he corregido ahora, gracias.
Lekensteyn

1
Desea ls ~/folder/que si ~/folderes un enlace simbólico lsenumere el contenido de su destino en lugar del enlace en sí. Por cierto, todos los lscomandos deben seguirse -1para que se imprima un solo archivo por línea y wc -lsea ​​una forma válida de contar archivos.
pablomme

@pablomme Eres sincero sobre el enlace simbólico, pero -1está implícito ya que la salida no es una terminal sino una tubería.
Lekensteyn

2

Me hacía la misma pregunta y terminé escribiendo un guión corto.

magicalUnionThing(Lo llamo append):

#!/bin/sh
cat /dev/stdin
$*

Hacer ese script ejecutable

chmod +x ./magicalUnionThing

Ahora hazlo tú

cat wordlist.txt |./magicalUnionThing ls ~/folder/* | wc -l

Que hace:

  • Enviar entrada estándar a salida estándar
  • Ejecutar argumento. $*devuelve todos los argumentos como una cadena. La salida de ese comando va a la salida estándar de script de forma predeterminada.

Entonces, el stdout de magicalUnionThing será su stdin + stdout del comando que se pasa como argumento.

Por supuesto, hay formas más simples, según otras respuestas.
Quizás esta alternativa puede ser útil en algunos casos.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.