Przekaż wynik poprzedniego polecenia do następnego jako argument

117

Mam polecenie, które wyprowadza dane do stdout ( command1 -p=aaa -v=bbb -i=4). Linia wyjściowa może mieć następującą wartość:

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

Chcę grepować ten wynik, aby zapisać tę „szybkość” (myślę, że fajka będzie tu przydatna). Na koniec chciałbym, aby ta szybkość była wartością parametru drugiego polecenia (powiedzmy command2 -t=${rate})

Z mojej strony wydaje się to trudne. Chciałbym wiedzieć lepiej, jak używać rur, grep, sed i tak dalej.

Próbowałem wielu takich kombinacji, ale mylę się z tymi:

$ command1 -p=aaa -v=bbb -i=4 | grep "rate" 2>&1 command2 -t="rate was "${rate}
Paweł
źródło

Odpowiedzi:

142

Mylisz dwa bardzo różne typy danych wejściowych.

  1. Standardowe wejście ( stdin)
  2. Argumenty wiersza poleceń

Są różne i są przydatne do różnych celów. Niektóre polecenia mogą pobierać dane na dwa sposoby, ale zwykle używają ich inaczej. Weźmy na przykład wcpolecenie:

  1. Przekazywanie danych wejściowych przez stdin:

    ls | wc -l
    

    To policzy linie na wyjściu ls

  2. Przekazywanie danych wejściowych przez argumenty wiersza poleceń:

    wc -l $(ls)
    

    To policzy linie na liście plików wydrukowanych przezls

Zupełnie inne rzeczy.

Aby odpowiedzieć na twoje pytanie, brzmi to tak, jakbyś chciał uchwycić szybkość z wyjścia pierwszego polecenia, a następnie użyć stawki jako argumentu linii poleceń dla drugiego polecenia. Oto jeden ze sposobów, aby to zrobić:

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

Wyjaśnienie sed:

  • s/pattern/replacement/Komenda ma zastąpić jakiś wzór
  • Wzorzec oznacza: wiersz musi zaczynać się od „rate” ( ^rate), po którym następują dowolne dwa znaki ( ..), następnie 0 lub więcej cyfr, po których następuje a %, a następnie reszta tekstu ( .*)
  • \1w zastępstwie oznacza treść pierwszego wyrażenia uchwyconego w środku \(...\), więc w tym przypadku cyfry przed %znakiem
  • -nFlaga sedpolecenia znaczy nie drukować linie domyślnie. Na pkońcu s///polecenia oznacza wydrukowanie linii, jeśli nastąpiła zamiana. Krótko mówiąc, polecenie wydrukuje coś tylko, jeśli było dopasowanie.
janos
źródło
12

Zwykle używam tego:

command1 | xargs -I{} command2 {}

Przekaż wyjście command1przez xargs używając podstawienia (nawiasy klamrowe) do command2. Jeśli polecenie1 jest findkonieczne, należy użyć -print0i dodać -0do xargsciągów zakończonych zerem i xargswywoła command2każdą znalezioną rzecz.

W twoim przypadku (i biorąc linię sed z @Janos):

command1 -p=aaa -v=bbb -i=4 | sed -ne 's/^rate..\([0-9]*\)%.*/\1/p' | xargs -I{} command2 -t="rate was {}"
Michael Anderson
źródło
Dzięki temu wszystko jest fajne i fajne, dlatego to lubię.
Mateen Ulhaq
4

Aby zasymulować dane wyjściowe command1używam tej instrukcji echa:

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

Szybki test:

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

To wszystko dobrze, więc przeanalizujmy to:

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

Więc otrzymujemy linię, z której chcemy command1, przepuszczamy to do command2:

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

Spodziewam "rate was "$(command1 | grep 'rate')się konkatenacji automatycznie. Jeśli to nie działa z powodu białych znaków, powinieneś być w stanie przekazać dane wejściowe w ten sposób:

$ alias command2='echo'
$ command2 -t=$(echo '"rate was ' $(command1 | grep 'rate') '"')
-t="rate was rate (10%) - name: value - 10Kbps "
użytkownik56452
źródło
3

Spróbuj tego za pomocą & :

command | grep -oP '\$>\s+rate\s+\(\K[^\)]+'
Gilles Quenot
źródło
2

Pierwszym zadaniem jest wyodrębnienie stawki z tej linii. W GNU grep (niewbudowany Linux lub Cygwin) możesz skorzystać z tej -oopcji. Część, którą chcesz, to ta, która zawiera tylko cyfry, a po niej %znak. Jeśli nie chcesz wyodrębnić %samego siebie, potrzebujesz dodatkowej sztuczki: asertywne stwierdzenie zerowej szerokości , które nie pasuje do niczego, ale tylko wtedy, gdy po nim nie następuje nic %.

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

Inną możliwością jest użycie sed. Aby wyodrębnić część linii w sed, użyj spolecenia z wyrażeniem regularnym pasującym do całej linii (zaczynając od ^i kończąc na $), z częścią do zachowania w grupie ( \(…\)). Zastąp całą linię zawartością grup, które chcesz zachować. Ogólnie rzecz biorąc, przekaż -nopcję wyłączenia domyślnego drukowania i wstaw pmodyfikator do drukowania linii, w których jest coś do wyodrębnienia (tutaj jest jedna linia, więc to nie ma znaczenia). Zobacz Zwracanie tylko części linii po pasującym wzorcu i Wyodrębnianie wyrażenia regularnego dopasowanego do „sed” bez drukowania otaczających znaków, aby uzyskać więcej sztuczek sed.

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

Bardziej elastyczny niż sed, jest awk. Awk wykonuje instrukcje dla każdej linii w małym imperatywnym języku. Istnieje wiele sposobów wyodrębnienia stawki; Zaznaczam drugie pola (pola są domyślnie ograniczone białymi spacjami) i usuwam wszystkie znaki, które nie są cyframi.

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

Następnym krokiem, po wyodrębnieniu stawki, jest przekazanie jej jako argumentu command2. Narzędziem do tego jest zawieszenie polecenia . Jeśli umieścisz polecenie w środku $(…)(nawias dolara), jego dane wyjściowe zostaną podstawione w linii poleceń. Dane wyjściowe polecenia są dzielone na osobne słowa w każdym bloku białych znaków, a każde słowo jest traktowane jako wzór wieloznaczny; chyba że chcesz to się stało, umieścić w cudzysłowie podstawienia polecenia: "$(…)". W przypadku podwójnych cudzysłowów dane wyjściowe polecenia są używane bezpośrednio jako pojedynczy parametr (jedyną transformacją jest to, że znaki nowej linii na końcu danych wyjściowych są usuwane).

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

Możesz używać, grepa to PCRE - wyrażenia regularne zgodne z Perlem. Pozwala to na użycie opcji lookbehind, aby dopasować wartość rate, bez włączania ciągu „rate” w wynikach podczas szukania go.

Przykład

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

Detale

Powyższe grepdziała w następujący sposób:

  • -ozwróci tylko to, czego szukamy \d+, czyli cyfry w parens.
  • -P włącza funkcję PCRE grep
  • (?<=^rate \() zwróci tylko ciągi rozpoczynające się od „rate (”

polecenie 2

Aby złapać wartość „stawki”, możesz uruchomić command1tak:

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

Następnie dla drugiego polecenia wystarczy użyć tej zmiennej.

$ command2 -t=${rate}

Możesz mieć ochotę i zrobić jedną linię:

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

Spowoduje to wykonanie polecenia 1 w $(..)bloku wykonawczym, pobranie jego wyników i włączenie ich do -t=..przełącznika polecenia 2.

slm
źródło
0

Zasadniczo używam `command` do umieszczenia jego wyniku jako argumentu w innym poleceniu. Na przykład, aby znaleźć zasoby zużywane przez proces foo na freebsd będzie:

procstat -r `pgrep -x foo`

Tutaj pgrep służy do wyodrębnienia PID procesu foo, który jest przekazywany do komendy procstat, która oczekuje PID procesu jako argumentu.

Vishal Sahu
źródło