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

119

Eu tenho um comando que gera 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 o valor de um parâmetro em um segundo comando (digamos command2 -t=${rate})

Parece ser complicado do meu lado; 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 elas:

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

Respostas:

144

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 fins. Alguns comandos podem receber entrada de ambos os modos, mas geralmente os usam de maneira diferente. Tome por exemplo o wccomando:

  1. Passagem de entrada por stdin:

    ls | wc -l
    

    Isso contará as linhas na saída de ls

  2. Passando entrada por argumentos de linha de comando:

    wc -l $(ls)
    

    Isso contará linhas na lista de arquivos impressos porls

Coisas completamente diferentes.

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

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

Explicação do sed:

  • O s/pattern/replacement/comando é substituir algum padrão
  • O padrão significa: a linha deve começar com "taxa" ( ^rate) seguida por dois caracteres ( ..), seguida por 0 ou mais dígitos, seguida por a %, seguida pelo resto do texto ( .*)
  • \1na substituição significa o conteúdo da primeira expressão capturada \(...\), portanto, neste caso, os dígitos antes do %sinal
  • O -nsinalizador do sedcomando significa não imprimir linhas por padrão. O pno final do s///comando significa imprimir a linha se houver uma substituição. Em resumo, o comando imprimirá algo apenas se houver uma correspondência.
janos
fonte
13

Eu costumo usar isso:

command1 | xargs -I{} command2 {}

Passe a saída de command1através de xargs usando substituição (os chavetas) para command2. Se command1 não findse esqueça de usar -print0e adicionar -0para xargscadeias terminadas nulas e xargschamará command2cada coisa encontrada.

No seu caso (e usando a linha sed de @Janos):

command1 -p=aaa -v=bbb -i=4 | sed -ne 's/^rate..\([0-9]*\)%.*/\1/p' | xargs -I{} command2 -t="rate was {}"
Michael Anderson
fonte
Isso mantém as coisas agradáveis ​​e é por isso que eu gosto disso.
Mateen Ulhaq
4

Para simular a saída de command1estou usando esta 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

Tudo bem, então vamos analisar:

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

Então, obtemos a linha que queremos command1, vamos passar para command2:

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

Estou esperando "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 "
user56452
fonte
3

Tente fazer isso usando & :

command | grep -oP '\$>\s+rate\s+\(\K[^\)]+'
Gilles Quenot
fonte
2

A primeira tarefa é extrair a taxa dessa linha. Com o GNU grep (Linux ou Cygwin não incorporado), você pode usar a -oopção A parte que você deseja é aquela que contém apenas dígitos e seguida por um %sinal. Se você não deseja extrair o %próprio, precisará de um truque adicional: uma asserção lookahead de largura zero , que não corresponde a nada, mas apenas se esse nada for seguido por %.

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

Outra possibilidade é usar o sed. Para extrair uma parte de uma linha no sed, use o scomando, com um regex que corresponda à linha inteira (iniciando ^e terminando com $), com a parte a ser mantida em um grupo ( \(…\)). Substitua a linha inteira pelo conteúdo do (s) grupo (s) a manter. Em geral, passe a -nopção de desativar a impressão padrão e coloque o pmodificador para imprimir linhas onde houver algo a extrair (aqui há uma única linha para que isso não importe). Consulte Retornar apenas a parte de uma linha após um padrão correspondente e Extraindo uma regex correspondente a 'sed' sem imprimir os caracteres ao redor para obter mais truques de sed.

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

Mais flexível do que sed, é estranho. O Awk executa instruções para cada linha em uma pequena linguagem imperativa. Existem muitas maneiras de extrair a taxa aqui; Seleciono os segundos campos (os campos são delimitados por espaço 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 argumento para command2. A ferramenta para isso é uma suspeita de comando . Se você colocar um comando dentro $(…)(parênteses em dólar), 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ê quer que isso aconteça, coloque aspas em torno da substituição de comando: "$(…)". Com 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]*\)%).*$/\1/')"
Gilles
fonte
0

Você pode usar grepe suas expressões regulares compatíveis com PCRE - Perl. Isso permite que você utilize um lookbehind para corresponder ao valor da taxa, sem incluir a string "taxa" nos seus resultados ao cumpri-la.

Exemplo

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

Detalhes

O acima grepfunciona da seguinte maneira:

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

command2

Para capturar o valor de "rate", você pode executar da seguinte command1maneira:

$ 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 ser extravagante e criar uma linha única:

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

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

slm
fonte
0

Eu geralmente uso `command` para colocar sua saída como argumento para outro comando. Por exemplo, para encontrar recursos consumidos pelo processo foo no freebsd será:

procstat -r `pgrep -x foo`

Aqui, o pgrep é usado para extrair o PID do processo foo, que é passado ao comando procstat, que espera o PID do processo como argumento.

Vishal Sahu
fonte