Pase el resultado del comando anterior al siguiente como argumento


119

Tengo un comando que envía datos a stdout ( command1 -p=aaa -v=bbb -i=4). La línea de salida puede tener el siguiente valor:

rate (10%) - name: value - 10Kbps

Quiero grep esa salida para almacenar esa 'tasa' (supongo que la tubería será útil aquí). Y finalmente, me gustaría que esa tasa sea un valor de un parámetro en un segundo comando (digamos command2 -t=${rate})

Parece ser complicado de mi parte; Me gustaría saber mejor cómo usar tuberías, grep, sed, etc.

He probado muchas combinaciones como esa, pero me estoy confundiendo con estas:

$ command1 -p=aaa -v=bbb -i=4 | grep "rate" 2>&1 command2 -t="rate was "${rate}

Respuestas:


144

Está confundiendo dos tipos muy diferentes de entradas.

  1. Entrada estándar ( stdin)
  2. Argumentos de línea de comando

Estos son diferentes y son útiles para diferentes propósitos. Algunos comandos pueden recibir información de ambas maneras, pero generalmente los usan de manera diferente. Tome por ejemplo el wccomando:

  1. Pasando entrada por stdin:

    ls | wc -l
    

    Esto contará las líneas en la salida de ls

  2. Pasar entrada por argumentos de línea de comando:

    wc -l $(ls)
    

    Esto contará líneas en la lista de archivos impresos porls

Cosas completamente diferentes.

Para responder a su pregunta, parece que desea capturar la velocidad de la salida del primer comando y luego usar la velocidad como argumento de línea de comando para el segundo comando. Aquí hay una forma de hacer eso:

rate=$(command1 | sed -ne 's/^rate..\([0-9]*\)%.*/\1/p')
command2 -t "rate was $rate"

Explicación de sed:

  • El s/pattern/replacement/comando es reemplazar algún patrón
  • El patrón significa: la línea debe comenzar con "rate" ( ^rate) seguido de dos caracteres ( ..), seguido de 0 o más dígitos, seguido de a %, seguido del resto del texto ( .*)
  • \1en el reemplazo significa el contenido de la primera expresión capturada dentro \(...\), por lo que en este caso los dígitos antes del %signo
  • La -nbandera del sedcomando significa no imprimir líneas por defecto. Al pfinal del s///comando significa imprimir la línea si hubo un reemplazo. En resumen, el comando imprimirá algo solo si hubo una coincidencia.

13

Tiendo a usar esto:

command1 | xargs -I{} command2 {}

Pase la salida de command1xargs a través de la sustitución (las llaves) a command2. Si comando1 es findestar seguro de usar -print0y añadir -0a xargspor terminada en nulo cuerdas y xargsllamará command2para cada cosa encontrado.

En su caso (y tomando la línea sed de @Janos):

command1 -p=aaa -v=bbb -i=4 | sed -ne 's/^rate..\([0-9]*\)%.*/\1/p' | xargs -I{} command2 -t="rate was {}"

Esto mantiene las cosas bonitas y perfectas, por eso me gusta esto.
Mateen Ulhaq

4

Para simular la salida de command1Estoy usando esta declaración de eco:

$ echo -e "Foo\nrate (10%) - name: value - 10Kbps\nBar"
$ alias command1='echo -e "Blah\nrate (10%) - name: value - 10Kbps\nBlag"'

Una prueba rápida:

$ command1
Blah
rate (10%) - name: value - 10Kbps
Blag

Eso está bien, así que analicemos:

$ command1 | grep 'rate'
rate (10%) - name: value - 10Kbps

Entonces obtenemos la línea que queremos, pasemos command1eso a command2:

$ alias command2='echo'
$ command2 -t="rate was "$(command1 | grep 'rate')
-t=rate was rate (10%) - name: value - 10Kbps

Espero "rate was "$(command1 | grep 'rate')concatenar automáticamente. Si eso no funciona debido al espacio en blanco, debería poder pasar la entrada de esta manera:

$ alias command2='echo'
$ command2 -t=$(echo '"rate was ' $(command1 | grep 'rate') '"')
-t="rate was rate (10%) - name: value - 10Kbps "


2

La primera tarea es extraer la tasa de esa línea. Con GNU grep (Linux no integrado o Cygwin), puede usar la -oopción. La parte que desea es la que contiene solo dígitos, seguida de un %signo. Si no desea extraer el %propio, necesita un truco adicional: una aserción anticipada de ancho cero , que no coincide con nada, pero solo si no se sigue nada %.

command1 -p=aaa -v=bbb -i=4 | grep -o -P '[0-9]+(?=%)'

Otra posibilidad es usar sed. Para extraer una parte de una línea en sed, use el scomando, con una expresión regular que coincida con la línea completa (comenzando ^y terminando con $), con la parte para retener en un grupo ( \(…\)). Reemplace toda la línea por el contenido de los grupos que desea conservar. En general, pase la -nopción de desactivar la impresión predeterminada y coloque el pmodificador para imprimir líneas donde haya algo que extraer (aquí hay una sola línea, por lo que no importa). Consulte Devolver solo la parte de una línea después de un patrón coincidente y Extraer una expresión regular combinada con 'sed' sin imprimir los caracteres circundantes para obtener más trucos sed.

command1 -p=aaa -v=bbb -i=4 | sed 's/^.*rate(\([0-9]*\)%).*$/\1/'

Más flexible de nuevo que sed, es awk. Awk ejecuta instrucciones para cada línea en un pequeño lenguaje imperativo. Hay muchas formas de extraer la tasa aquí; Selecciono los segundos campos (los campos están delimitados por espacios en blanco de forma predeterminada) y elimino todos los caracteres que no son un dígito.

command1 -p=aaa -v=bbb -i=4 | awk '{gsub(/[^0-9]+/, "", $2); print $2}'

El siguiente paso, ahora que ha extraído la tasa, es pasarla como argumento a command2. La herramienta para eso es una sustitución de comandos . Si coloca un comando dentro $(…)(paréntesis dólar), su salida se sustituye en la línea de comando. La salida del comando se divide en palabras separadas en cada bloque de espacios en blanco, y cada palabra se trata como un patrón comodín; a menos que usted quiere que esto suceda, ponga comillas alrededor de la sustitución de comandos: "$(…)". Con las comillas dobles, la salida del comando se usa directamente como un parámetro único (la única transformación es que se eliminan las nuevas líneas al final de la salida).

command2 -t "$(command1 -p=aaa -v=bbb -i=4 |
               sed 's/^.*rate(\([0-9]*\)%).*$/\1/')"

0

Puede usar grepy es PCRE - Expresiones regulares compatibles con Perl. Esto le permite utilizar una mirada retrospectiva para que coincida con el valor de la tasa, sin incluir la cadena "tasa" dentro de sus resultados cuando lo busque.

Ejemplo

$ echo "rate (10%) - name: value - 10Kbps" | grep -oP '(?<=^rate \()\d+'
10

Detalles

Lo anterior grepfunciona de la siguiente manera:

  • -odevolverá solo lo que estamos buscando \d+, que son los dígitos dentro de los parens.
  • -P habilita la función PCRE de grep
  • (?<=^rate \() solo devolverá cadenas que comiencen con "rate ("

comando2

Para atrapar el valor de "tasa" puede ejecutar command1así:

$ rate=$(command 1 | grep -oP '(?<=^rate \()\d+'

Luego, para su segundo comando, simplemente usaría esa variable.

$ command2 -t=${rate}

Podrías ponerte elegante y hacer que todo sea una línea:

$ command2 -t=$(command1 | grep -oP '(?<=^rate \()\d+')

Esto ejecutará command1 dentro del $(..)bloque de ejecución, tomará sus resultados e los incluirá en el -t=..interruptor de command2 .


0

Generalmente uso `command` para colocar su salida como argumento para otro comando. Por ejemplo, para encontrar el recurso consumido por el proceso foo en freebsd será:

procstat -r `pgrep -x foo`

Aquí, pgrep se usa para extraer el PID del proceso foo que se pasa al comando procstat que espera el PID del proceso como argumento.

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.