Wie kann ein einzelnes Element von einer Befehlsausgabe an einen anderen Befehl übergeben werden?

455
jewbix.cube

Ein einfaches Beispiel mit grep (Beachten Sie, dass grep nicht mein einziger Anwendungsfall dafür ist):

$ grep -Irl "foo" path/to/directory/help.js path/to/directory/config.js path/to/directory/task.js 

Nun möchte ich die config.jsDatei in Vi öffnen . Meine normale Methode wäre:

$ vi path/to/directory/config.js 

die ich entweder mit der tab-Vervollständigung von Hand eingeben oder den Dateinamen aus dem grep-Ergebnis hervorheben und kopieren / einfügen habe.

Ich möchte die Datei jedoch nur durch Angabe des zweiten Ergebnisses des grep-Befehls anzeigen lassen. So etwas wie:

$ grep -Irl "foo" | xargs vi 2 

Offensichtlich würde xargs nicht so funktionieren, war nur ein Beispiel. Aber ich versuche herauszufinden, ob es eine Möglichkeit gibt, xargs (oder ein anderes Dienstprogramm) zu verwenden, um dies zu erreichen, und ich finde es nicht.

Etwas, was mir ein Teamkollege vorschlug, war zu verwenden headund tailzusammen, wie folgt:

$ grep -Irl "foo" | tail -n 1 | xargs vi 

würde task.js abrufen und

$ grep -Irl "foo" | head -n 2 | tail -n 1 | xargs vi 

würde config.js abrufen. Ich frage mich, ob es eine weniger ausführliche Methode gibt.

3
Die Auswahl nach Zeilennummern erscheint merkwürdig. Woher kennen Sie die Zeilennummer, wenn Sie die Ausgabe nicht zuerst auf dem Terminal angezeigt haben? Und dann müssen Sie Zeilen zählen, um herauszufinden, was Sie wollen. Warum nicht "grep -Irl" foo "Pfad | grep config`? Barmar vor 6 Jahren 1
Es ist unwahrscheinlich, dass ich weiß, dass `config.js` existiert, bevor ich das mache, also würde ich zuerst das grep ausführen. Wenn ich dann eine Datei sehe, zu der ich etwas tun möchte, würde ich den Pfeil nach oben drücken, um den Befehl erneut auszuführen, und das Ergebnis an etwas weiterleiten, das die gewünschte Datei öffnen würde. Das Ziel ist es, zu vermeiden, dass Sie mit der Maus einen Dateipfad kopieren / einfügen. Ein Mitarbeiter zeigte mir, dass ich auch `screen` verwenden kann, um Text über eine Tastatur an einer beliebigen Stelle auf dem Bildschirm auszuwählen. Ich weiß, dass dies möglicherweise nicht viel Zeit spart, insbesondere wenn die Zeilen zu viele sind, um gezählt zu werden, und jetzt muss ich Zeilennummern in meiner Ausgabe anzeigen. jewbix.cube vor 6 Jahren 0

3 Antworten auf die Frage

2
nickcrabtree

Nicht ganz das, was gefragt wurde, weil es keine Pipeline in vi verwendet, sondern sed :

vi $( grep -Irl "foo" | sed -n '2 p')

Nichts hindert dies daran, indem Sie in vi - grep - Irl "foo" | sed -n '2 p' | xargs vi davidgo vor 6 Jahren 1
Jetzt muss ich zwischen sed vs. awk stimmen. Scheint, dass awk im Allgemeinen bevorzugt ist, da es alles tun kann, was sed noch mehr kann? Ich bevorzuge die awk-Methode auch syntaktisch. https://stackoverflow.com/questions/1632113/was-is -der-differenz-zwischen-sand-and-awk jewbix.cube vor 6 Jahren 0
1
davidgo

Wie wäre es mit

 vi $( grep -IrL "foo" | awk "NR==3" ) 

Dies verwendet AWK, um die entsprechende Zeile zu finden. Die Ausgabe wird dann als Befehlszeilenargument für vi verwendet.

Ich bin oldschool, also bevorzuge ich das Äquivalent, aber die Version ist etwas verpönt.

 vi ` grep -IrL "foo" | awk "NR==3" ` 
Warum ist die Backtick-Version eher verpönt? Ein Tastendruck weniger für meine Zwecke ... jewbix.cube vor 6 Jahren 0
Lesen Sie https://unix.stackexchange.com/questions/126927/have-backticks-ie-cmd-in-sh-shells-been-deprecated#126928 (und vergessen Sie nicht, meine Antwort zu bestätigen, wenn Sie möchten es! davidgo vor 6 Jahren 1
1
mpy

Ich habe für diesen Zweck eine Wrapper-Funktion geschrieben, die Sie verwenden können, wenn Sie eine Ausgabezeile eines Befehls wiederverwenden möchten.

Zuerst die Funktion für Bash :

catch() { out="$($@)"  nl <<< "$out" read -a r -d '\n' <<< "$out" r=("dummy" "$") } 

Dann für zsh :

catch () { out="$($@)"  nl <<< "$out" r=("${(@f)out}")  } 

Die Verwendung ist für beide Varianten gleich. Führen Sie zuerst den gewünschten Befehl aus, der mit catch vorangestellt wird, wodurch die Befehlsausgabe mit einer schönen Nummerierung angezeigt wird:

$ catch grep -Irl "foo" 1 path/to/dir/task.js 2 path/to/dir/help.js 3 path/to/dir/config.js $ 

Jetzt können Sie jede Zeile in einem folgenden Befehl wiederverwenden, indem Sie den Parameter array verwenden $r, z

$ wc -c $ 14044 path/to/dir/help.js 

In zsh kannst du die geschweiften Klammern ( wc -c $r[2]) weglassen, aber in bash brauchst du sie leider.

Einige Vorbehalte, die mir in den Sinn kommen:

  • Die Ausgabe wird erst angezeigt, wenn der Befehl abgeschlossen ist
  • seltsame Zeichen in der Ausgabe können unregelmäßiges Verhalten erzeugen (z. B. Zeilenumbrüche in Dateinamen)
  • Nur Ausgabe an STDOUT wird erfasst (wenn eine Ausgabe in STDERR geschrieben wird, wird sie auf der Konsole nicht gemischt und ignoriert $r).