Passa l'output del comando precedente al successivo come argomento


119

Ho un comando che invia i dati a stdout ( command1 -p=aaa -v=bbb -i=4). La linea di uscita può avere il seguente valore:

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

Voglio grep quell'output per memorizzare quel 'rate' (immagino che pipe sarà utile qui). E infine, vorrei che quel rate fosse il valore di un parametro su un secondo comando (diciamo command2 -t=${rate})

Sembra essere complicato dalla mia parte; Vorrei sapere meglio come usare pipe, grep, sed e così via.

Ho provato molte combinazioni come quella ma mi sto confondendo:

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

Risposte:


144

Stai confondendo due tipi molto diversi di input.

  1. Input standard ( stdin)
  2. Argomenti della riga di comando

Questi sono diversi e sono utili per scopi diversi. Alcuni comandi possono ricevere input in entrambi i modi, ma in genere li usano in modo diverso. Prendi ad esempio il wccomando:

  1. Passando input da stdin:

    ls | wc -l
    

    Questo conterà le linee nell'output di ls

  2. Passando input dagli argomenti della riga di comando:

    wc -l $(ls)
    

    Questo conterà le righe nell'elenco dei file stampati dals

Cose completamente diverse.

Per rispondere alla tua domanda, sembra che tu voglia catturare la velocità dall'output del primo comando e quindi usare la frequenza come argomento della riga di comando per il secondo comando. Ecco un modo per farlo:

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

Spiegazione di sed:

  • Il s/pattern/replacement/comando è di sostituire alcuni pattern
  • Il modello significa: la riga deve iniziare con "rate" ( ^rate) seguito da due caratteri ( ..), seguito da 0 o più cifre, seguito da a %, seguito dal resto del testo ( .*)
  • \1nella sostituzione si intende il contenuto della prima espressione catturata all'interno \(...\), quindi in questo caso le cifre prima del %segno
  • Il -nflag del sedcomando indica di non stampare le linee per impostazione predefinita. Alla pfine del s///comando si intende stampare la riga in caso di sostituzione. In breve, il comando stamperà qualcosa solo se ci fosse una corrispondenza.

13

Tendo a usare questo:

command1 | xargs -I{} command2 {}

Passa l'output di command1xargs usando la sostituzione (le parentesi graffe) a command2. Se command1 è findsicuro di usare -print0e aggiungere -0a xargsstringhe con terminazione null e xargschiamerà command2per ogni cosa trovata.

Nel tuo caso (e prendendo la linea sed da @Janos):

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

Questo mantiene le cose belle e curate, motivo per cui mi piace.
Mateen Ulhaq,

4

Per simulare l'output di command1sto usando questa dichiarazione echo:

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

Un test rapido:

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

Tutto bene, quindi analizziamolo:

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

Quindi otteniamo la linea da cui vogliamo command1, passiamo a command2:

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

Mi aspetto "rate was "$(command1 | grep 'rate')di concatenare automaticamente. Se ciò non funziona a causa degli spazi bianchi, dovresti invece essere in grado di passare l'input in questo modo:

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


2

Il primo compito è quello di estrarre il tasso da quella linea. Con GNU grep (Linux non incorporato o Cygwin), puoi usare l' -oopzione. La parte desiderata è quella contenente solo cifre e seguita da un %segno. Se non vuoi estrarre lo %stesso, hai bisogno di un trucco aggiuntivo: un'asserzione lookahead a larghezza zero , che non corrisponde a nulla ma solo se quel nulla è seguito da %.

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

Un'altra possibilità è usare sed. Per estrarre una parte di una linea in sed, usa il scomando, con una regex che corrisponde all'intera linea (che inizia con ^e termina con $), con la parte da conservare in un gruppo ( \(…\)). Sostituisci l'intera riga con il contenuto dei gruppi da conservare. In generale, passa l' -nopzione per disattivare la stampa predefinita e metti il pmodificatore per stampare le linee in cui c'è qualcosa da estrarre (qui c'è una sola linea quindi non importa). Consultate Restituire solo la parte di una linea dopo un modello di corrispondenza ed Estrarre una regex abbinata a 'sed' senza stampare i caratteri circostanti per ulteriori trucchi sed.

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

Ancora più flessibile di sed, è stupido. Awk esegue le istruzioni per ogni riga in un linguaggio imperativo di piccole dimensioni. Ci sono molti modi per estrarre la tariffa qui; Seleziono i secondi campi (i campi sono delimitati da spazi bianchi per impostazione predefinita) e rimuovo tutti i caratteri che non sono una cifra.

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

Il passaggio successivo, ora che hai estratto il tasso, è di passarlo come argomento a command2. Lo strumento per questo è una sospensione del comando . Se si inserisce un comando all'interno $(…)(parentesi in dollari), il suo output viene sostituito nella riga di comando. L'output del comando viene suddiviso in parole separate in ciascun blocco di spazi bianchi e ogni parola viene trattata come un modello jolly; a meno che non si vuole che questo accada, mettere le virgolette attorno alla sostituzione di comando: "$(…)". Con le doppie virgolette, l'output del comando viene utilizzato direttamente come singolo parametro (l'unica trasformazione è che le nuove righe alla fine dell'output vengono rimosse).

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

0

È possibile utilizzare greped è PCRE - Espressioni regolari compatibili Perl. Ciò consente di utilizzare un lookbehind per abbinare il valore di rate, senza includere la stringa "rate" nei risultati quando si esegue il grepping per esso.

Esempio

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

Dettagli

Quanto sopra grepfunziona come segue:

  • -orestituirà solo ciò che stiamo cercando \d+, ovvero le cifre all'interno delle parentesi.
  • -P abilita la funzionalità PCRE di grep
  • (?<=^rate \() restituirà solo stringhe che iniziano con "rate ("

comando2

Per ottenere il valore di "rate" puoi eseguire in questo command1modo:

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

Quindi per il tuo secondo comando useresti semplicemente quella variabile.

$ command2 -t=${rate}

Potresti avere fantasia e fare tutto in una riga:

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

Questo eseguirà command1 all'interno del $(..)blocco di esecuzione, prenderà i suoi risultati e li includerà nello -t=..switch command2 .


0

In genere uso `command` per posizionare l'output come argomento di un altro comando. Ad esempio, per trovare le risorse consumate dal processo foo su freebsd saranno:

procstat -r `pgrep -x foo`

Qui, pgrep viene utilizzato per estrarre il PID del processo foo che viene passato al comando procstat che prevede il PID del processo come argomento.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.