Passa a saída do comando anterior para o próximo como um argumento

83

Eu tenho um comando que envia dados para stdout ( command1 -p=aaa -v=bbb -i=4 ). A linha de saída pode ter o seguinte valor:

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

Eu quero grep essa saída para armazenar essa 'taxa' (acho que o pipe será útil aqui). E, finalmente, gostaria que essa taxa fosse um valor de um parâmetro em um segundo comando (digamos command2 -t=${rate} )

Parece ser complicado do meu lado; Eu gostaria de saber melhor como usar pipes, grep, sed e assim por diante.

Eu tentei muitas combinações como essa, mas estou ficando confuso com isso:

$ command1 -p=aaa -v=bbb -i=4 | grep "rate" 2>&1 command2 -t="rate was "${rate}
    
por Paul 10.01.2014 / 21:14

5 respostas

108

Você está confundindo dois tipos muito diferentes de entradas.

  1. Entrada padrão ( stdin )
  2. Argumentos da linha de comando

Estes são diferentes e são úteis para diferentes propósitos. Alguns comandos podem receber entradas nos dois sentidos, mas eles normalmente os usam de maneira diferente. Tomemos por exemplo o comando wc :

  1. Transmitindo entrada por stdin :

    ls | wc -l
    

    Isso contará as linhas na saída de ls

  2. Transmitindo entrada pelos argumentos da linha de comando:

    wc -l $(ls)
    

    Isso contará as linhas na lista de arquivos impressos por ls

Coisas completamente diferentes.

Para responder à sua pergunta, parece que você deseja capturar a taxa da saída do primeiro comando e usar a taxa como um argumento de linha de comando para o segundo comando. Aqui está uma maneira de fazer isso:

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

Explicação do sed :

  • O comando s/pattern/replacement/ substitui algum padrão
  • O padrão significa: a linha deve começar com "taxa" ( ^rate ) seguida por qualquer caractere dois ( .. ), seguido por 0 ou mais dígitos, seguido por um % , seguido pelo restante o texto ( .* )
  • na substituição significa o conteúdo da primeira expressão capturada em \(...\) , portanto, neste caso, os dígitos antes do sinal %
  • O sinalizador -n do comando sed significa não imprimir linhas por padrão. O p no final do comando s/// significa imprimir a linha se houver uma substituição. Em suma, o comando imprimirá algo apenas se houver uma correspondência.
por 10.01.2014 / 22:34
4

Para simular a saída de command1 , estou usando essa instrução echo:

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

Um teste rápido:

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

Isso é tudo de bom, então vamos analisar:

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

Portanto, obtemos a linha desejada de command1 , vamos passar isso para 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 automaticamente. Se isso não funcionar por causa do espaço em branco, você poderá passar a entrada assim:

$ alias command2='echo'
$ command2 -t=$(echo '"rate was ' $(command1 | grep 'rate') '"')
-t="rate was rate (10%) - name: value - 10Kbps "
    
por 10.01.2014 / 23:08
3

Tente isso usando & :

command | grep -oP '\$>\s+rate\s+\(\K[^\)]+'
    
por 10.01.2014 / 21:23
2

A primeira tarefa é extrair a taxa dessa linha. Com o GNU grep (Linux ou Cygwin não integrado), você pode usar a opção -o . A parte que você quer é a que contém apenas dígitos e seguida por um sinal % . Se você não quiser extrair o % , você precisa de um truque adicional: um zero- width lookahead assertion , que não combina com nada, mas somente se nada for seguido por % .

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

Outra possibilidade é usar sed. Para extrair uma parte de uma linha em sed, use o comando s , com uma expressão regular que corresponde à linha inteira (começando com ^ e terminando com $ ), com a parte a reter em um grupo ( \(…\) ). Substitua a linha inteira pelo conteúdo do (s) grupo (s) a manter. Em geral, passe a opção -n para desativar a impressão padrão e coloque o modificador p para imprimir linhas onde há algo para extrair (aqui há uma única linha, então não importa). Consulte Retornar somente a parte de uma linha após um padrão correspondente e Extraindo um regex que corresponde a 'sed' sem imprimir os caracteres adjacentes para mais truques de sed.

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

Mais flexível que o sed, é awk. Awk executa instruções para cada linha em um pequeno idioma imperativo. Há muitas maneiras de extrair a taxa aqui; Eu seleciono os segundos campos (os campos são delimitados por espaços em branco por padrão) e removo todos os caracteres que não são um dígito.

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

O próximo passo, agora que você extraiu a taxa, é passá-la como um argumento para command2 . A ferramenta para isso é um comando susbtitution . Se você colocar um comando dentro de $(…) (dólar-parênteses), sua saída será substituída na linha de comando. A saída do comando é dividida em palavras separadas em cada bloco de espaço em branco, e cada palavra é tratada como um padrão curinga; a menos que você queira que isso aconteça, coloque aspas duplas em torno da substituição do comando: "$(…)" . Com as aspas duplas, a saída do comando é usada diretamente como um único parâmetro (a única transformação é que as novas linhas no final da saída são removidas).

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

Você pode usar grep e é PCRE - Perl Compatible Regular Expressions. Isso permite que você utilize um lookbehind para corresponder ao valor da taxa, sem incluir a string "rate" em seus resultados ao aplicá-lo.

Exemplo

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

Detalhes

O grep acima funciona da seguinte forma:

  • -o retornará apenas o que estamos procurando, \d+ , que são os dígitos dentro dos parênteses.
  • -P ativa o recurso PCRE do grep
  • (?<=^rate \() só retornará strings que começam com "rate ("

command2

Para pegar o valor de "rate", você pode executar command1 assim:

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

Então, para o seu segundo comando, você simplesmente usaria essa variável.

$ command2 -t=${rate}

Você pode ficar chique e fazer tudo isso em uma linha:

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

Isso executará o comando1 dentro do bloco de execução $(..) , obterá seus resultados e os incluirá na opção -t=.. do comando2.

    
por 11.01.2014 / 03:12