Übergeben Sie die Ausgabe des vorherigen Befehls als Argument an den nächsten


119

Ich habe einen Befehl, der Daten an stdout ( command1 -p=aaa -v=bbb -i=4) ausgibt . Die Ausgabezeile kann den folgenden Wert haben:

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

Ich möchte diese Ausgabe aufgreifen, um diese 'Rate' zu speichern (ich denke, Pipe wird hier nützlich sein). Und schließlich möchte ich, dass diese Rate ein Wert eines Parameters in einem zweiten Befehl ist (sagen wir mal command2 -t=${rate})

Es scheint auf meiner Seite knifflig zu sein; Ich würde gerne besser wissen, wie man Pfeifen, grep, sed und so weiter benutzt.

Ich habe viele Kombinationen wie diese ausprobiert, aber ich bin immer verwirrt von diesen:

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

Antworten:


144

Sie verwechseln zwei sehr unterschiedliche Arten von Eingaben.

  1. Standardeingabe ( stdin)
  2. Kommandozeilenargumente

Diese sind unterschiedlich und für verschiedene Zwecke nützlich. Einige Befehle können auf beide Arten eingegeben werden, werden jedoch normalerweise unterschiedlich verwendet. Nehmen Sie zum Beispiel den wcBefehl:

  1. Eingabe übergeben von stdin:

    ls | wc -l
    

    Dies zählt die Zeilen in der Ausgabe von ls

  2. Eingabe über Befehlszeilenargumente übergeben:

    wc -l $(ls)
    

    Dies zählt die Zeilen in der Liste der von gedruckten Dateienls

Ganz andere Dinge.

Zur Beantwortung Ihrer Frage möchten Sie die Rate aus der Ausgabe des ersten Befehls erfassen und sie dann als Befehlszeilenargument für den zweiten Befehl verwenden. Hier ist eine Möglichkeit, dies zu tun:

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

Erklärung der sed:

  • Der s/pattern/replacement/Befehl besteht darin, ein Muster zu ersetzen
  • Das Muster bedeutet: Die Zeile muss mit "rate" ( ^rate) beginnen, gefolgt von zwei beliebigen Zeichen ( ..), gefolgt von 0 oder mehr Ziffern, gefolgt von einem %, gefolgt vom Rest des Textes ( .*).
  • \1In der Ersetzung steht der Inhalt des ersten Ausdrucks \(...\), also in diesem Fall die Ziffern vor dem %Vorzeichen
  • Das -nFlag des sedBefehls bedeutet, dass standardmäßig keine Zeilen gedruckt werden. Das pam Ende des s///Befehls bedeutet, die Zeile zu drucken, wenn es einen Ersatz gab. Kurz gesagt, der Befehl gibt nur dann etwas aus, wenn eine Übereinstimmung gefunden wurde.

13

Ich neige dazu, dies zu verwenden:

command1 | xargs -I{} command2 {}

Leiten Sie die Ausgabe von command1through xargs mit Hilfe der Substitution (der geschweiften Klammern) an weiter command2. Wenn command1 findsicher ist , dass es für nullterminierte Zeichenfolgen verwendet -print0und zu addiert wird -0, wird jedes gefundene Element aufgerufen .xargsxargscommand2

In deinem Fall (und nimm die sed-Linie von @Janos):

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

Dies hält die Dinge schön und pipe'd, weshalb ich das mag.
Mateen Ulhaq

4

So simulieren Sie die Ausgabe von command1Ich verwende diese Echo-Anweisung:

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

Ein kurzer Test:

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

Das ist alles gut, also lasst es uns analysieren:

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

Damit wir die Linie bekommen, aus der wir wollen command1, lasst uns das weitergeben an command2:

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

Ich erwarte "rate was "$(command1 | grep 'rate'), automatisch zu verketten. Wenn dies aufgrund von Leerzeichen nicht funktioniert, sollten Sie in der Lage sein, die Eingabe stattdessen wie folgt zu übergeben:

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


2

Die erste Aufgabe besteht darin, die Rate aus dieser Zeile zu extrahieren. Mit GNU grep (nicht eingebettetes Linux oder Cygwin) können Sie diese -oOption verwenden. Der gewünschte Teil enthält nur Ziffern, gefolgt von einem %Zeichen. Wenn Sie das nicht %selbst extrahieren möchten , benötigen Sie einen zusätzlichen Trick: eine Lookahead-Behauptung mit der Breite Null , die mit nichts übereinstimmt, aber nur dann, wenn diesem nichts folgt %.

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

Eine andere Möglichkeit ist die Verwendung von sed. Um einen Teil einer Zeile in sed zu extrahieren, verwenden Sie den sBefehl mit einer Regex, die mit der gesamten Zeile (beginnend mit ^und endend mit $) übereinstimmt , und dem Teil, der in einer Gruppe ( \(…\)) beibehalten werden soll . Ersetzen Sie die gesamte Zeile durch den Inhalt der beizubehaltenden Gruppe (n). Übergeben Sie im Allgemeinen die -nOption zum Deaktivieren des Standarddrucks und setzen Sie den pModifikator, um Zeilen zu drucken, in denen etwas zu extrahieren ist (hier gibt es eine einzelne Zeile, damit es keine Rolle spielt). Siehe Nur den Teil einer Linie nach einem übereinstimmenden Muster zurückgeben und einen mit 'sed' übereinstimmenden regulären Ausdruck extrahieren, ohne die umgebenden Zeichen zu drucken, um weitere sed-Tricks zu erhalten.

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

Noch flexibler als sed, ist awk. Awk führt Anweisungen für jede Zeile in einer kleinen imperativen Sprache aus. Es gibt viele Möglichkeiten, den Kurs hier zu ermitteln. Ich wähle die zweiten Felder aus (Felder werden standardmäßig durch Leerzeichen begrenzt) und entferne alle Zeichen, die keine Ziffern sind.

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

Der nächste Schritt, nachdem Sie die Rate extrahiert haben, besteht darin, sie als Argument an zu übergeben command2. Das Werkzeug dafür ist eine Befehlssuspension . Wenn Sie einen Befehl eingeben $(…)(in Dollar-Klammern), wird dessen Ausgabe in die Befehlszeile eingefügt. Die Ausgabe des Befehls wird in jedem Whitespace-Block in separate Wörter aufgeteilt, und jedes Wort wird als Platzhaltermuster behandelt. es sei denn , Sie dies geschehen soll, setzen Sie doppelte Anführungszeichen um den Befehl Substitution: "$(…)". Bei doppelten Anführungszeichen wird die Ausgabe des Befehls direkt als einzelner Parameter verwendet (die einzige Transformation besteht darin, dass Zeilenumbrüche am Ende der Ausgabe entfernt werden).

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

0

Sie können grepPCRE - Perl - kompatible reguläre Ausdrücke verwenden. Auf diese Weise können Sie einen Lookbehind verwenden, der dem Wert von rate entspricht, ohne die Zeichenfolge "rate" in Ihre Ergebnisse aufzunehmen, wenn Sie danach suchen.

Beispiel

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

Einzelheiten

Das oben grepGenannte funktioniert wie folgt:

  • -oEs wird nur das zurückgegeben, was wir suchen \d+. Dies sind die Ziffern innerhalb der Parens.
  • -P Aktiviert die PCRE-Funktion von grep
  • (?<=^rate \() gibt nur Strings zurück, die mit "rate (" beginnen

befehl2

Um den Wert von "rate" zu ermitteln, können Sie folgendermaßen vorgehen command1:

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

Dann würden Sie für Ihren 2. Befehl einfach diese Variable verwenden.

$ command2 -t=${rate}

Sie könnten Lust bekommen und das alles zu einer einzigen Zeile machen:

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

Dadurch wird Befehl1 innerhalb des $(..)Ausführungsblocks ausgeführt, die Ergebnisse werden übernommen und in den -t=..Schalter von Befehl2 aufgenommen .


0

Normalerweise verwende ich `command`, um die Ausgabe als Argument für einen anderen Befehl zu platzieren. Um beispielsweise die von process foo auf freebsd verbrauchte Ressource zu finden, gilt Folgendes:

procstat -r `pgrep -x foo`

Hier wird pgrep verwendet, um die PID des Prozesses foo zu extrahieren, die an den Befehl procstat übergeben wird, der die PID des Prozesses als Argument erwartet.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.