bash: kürzester Weg, um die n-te Ausgabespalte zu erhalten

165

Angenommen, Sie stoßen während Ihres Arbeitstages wiederholt auf die folgende Form der Spaltenausgabe eines Befehls in bash (in meinem Fall durch Ausführung svn stin meinem Rails-Arbeitsverzeichnis):

?       changes.patch
M       app/models/superman.rb
A       app/models/superwoman.rb

Um mit der Ausgabe Ihres Befehls - in diesem Fall den Dateinamen - arbeiten zu können, ist eine Art Analyse erforderlich, damit die zweite Spalte als Eingabe für den nächsten Befehl verwendet werden kann.

Was ich getan habe, ist, awkum in die zweite Spalte zu gelangen, z. B. wenn ich alle Dateien entfernen möchte (nicht, dass dies ein typischer Anwendungsfall ist :), würde ich Folgendes tun:

svn st | awk '{print $2}' | xargs rm

Da ich dies oft tippe, ist eine natürliche Frage: Gibt es einen kürzeren (also cooleren) Weg, dies in Bash zu erreichen?

HINWEIS: Was ich frage, ist im Wesentlichen eine Shell-Befehlsfrage, obwohl sich mein konkretes Beispiel in meinem SVN-Workflow befindet. Wenn Sie der Meinung sind, dass der Workflow albern ist und einen alternativen Ansatz vorschlagen, werde ich Sie wahrscheinlich nicht ablehnen, andere jedoch, da die Frage hier wirklich lautet, wie die Ausgabe des Befehls für die n-te Spalte in bash so schnell wie möglich ausgeführt werden kann . Vielen Dank :)

Sv1
quelle
1
Wenn Sie einen Befehl häufig verwenden, erstellen Sie besser ein Skript und fügen es in Ihren Pfad ein. Sie können einfach eine Funktion in Ihrem Bashrc erstellen, wenn Sie es besser mögen. Ich sehe keinen Sinn darin, den Spaltenauswahlausdruck zu reduzieren.
Lynch
1
Sie haben Recht, und das könnte ich tun. Der 'Punkt' ist die Suche nach neuen Wegen, um Sachen in Bash zu machen, zum Lernen, aber hauptsächlich zum Spaß :)
Sv1
1
Außerdem haben Sie Ihre .bashrc nicht, wenn Sie irgendwo ssh-ing, daher ist es nützlich, sich ohne sie auszukennen.
Kos

Antworten:

125

Sie können verwenden, cutum auf das zweite Feld zuzugreifen:

cut -f2

Bearbeiten: Entschuldigung, ich habe nicht bemerkt, dass SVN keine Tabulatoren in seiner Ausgabe verwendet, also ist das ein bisschen nutzlos. Sie können cutdie Ausgabe anpassen , aber sie ist etwas zerbrechlich - so etwas wiecut -c 10- würde funktionieren, aber der genaue Wert hängt von Ihrem Setup ab.

Eine andere Option ist so etwas wie: sed 's/.\s\+//'

porges
quelle
1
Versuchen Sie, cut -f2mit der svn stAusgabe zu verwenden. Sie werden sehen, dass es nicht funktioniert.
Lynch
Ich nahm an, dass der Real svn stTabs in seiner Ausgabe verwenden würde. Nach einigen Tests scheint dies nicht der Fall zu sein. Verdammt.
porges
21
Durch Übergeben eines geeigneten Trennzeichens -dfunktioniert dies.
Yogh
6
Um das zu erweitern, was @Yogh gesagt hat, würde es für Leerzeichen als Trennzeichen so aussehen cut -d" " -f2.
Adamyonk
Ohne awk auf stdout benutze xargs: svn st | xargs | cut -d "" -f2
vr286
106

Um dasselbe zu erreichen wie:

svn st | awk '{print $2}' | xargs rm

Wenn Sie nur Bash verwenden, können Sie Folgendes verwenden:

svn st | while read a b; do rm "$b"; done

Zugegeben, es ist nicht kürzer, aber etwas effizienter und behandelt Leerzeichen in Ihren Dateinamen korrekt.

Rick Sladkey
quelle
Was ist aund bwie bekommen sie?
Timo
1
@ Timo arepräsentiert die erste Spalte und brepräsentiert die verbleibenden Spalten. Wenn Sie die zweite Spalte drucken möchten, verwenden Sie read a b c;und verwenden Sie echostattdessen anstelle von rm. Ich habe dies verwendet, um eine Reihe von ID-Prozessen zu erhalten, die einem Grep-Muster folgten, damit ich sie alle unterbrechen konnte.
Matt Kleinsmith
36

Ich befand mich in der gleichen Situation und fügte meiner .profileDatei folgende Aliase hinzu :

alias c1="awk '{print \$1}'"
alias c2="awk '{print \$2}'"
alias c3="awk '{print \$3}'"
alias c4="awk '{print \$4}'"
alias c5="awk '{print \$5}'"
alias c6="awk '{print \$6}'"
alias c7="awk '{print \$7}'"
alias c8="awk '{print \$8}'"
alias c9="awk '{print \$9}'"

Was mir erlaubt, solche Dinge zu schreiben:

svn st | c2 | xargs rm
StackedCrooked
quelle
15
Bash-Funktionen sind normalerweise nützlicher. Ich habe das gemacht: function c() { awk "{print \$$1}" } Dann kannst du machen: svn st | c 2 | xargs rm
Aissen
8
Aber dann muss ich ein zusätzliches Leerzeichen eingeben. Das ist zu anstrengend :)
StackedCrooked
16

Versuchen Sie das zsh. Es unterstützt Suffix-Alias, sodass Sie X in Ihrer .zshrc definieren können

alias -g X="| cut -d' ' -f2"

dann können Sie tun:

cat file X

Sie können noch einen Schritt weiter gehen und es für die n-te Spalte definieren:

alias -g X2="| cut -d' ' -f2"
alias -g X1="| cut -d' ' -f1"
alias -g X3="| cut -d' ' -f3"

Dadurch wird die n-te Spalte der Datei "Datei" ausgegeben. Sie können dies auch für Grep-Ausgabe oder weniger Ausgabe tun. Dies ist sehr praktisch und ein Killer-Feature des zsh.

Sie können noch einen Schritt weiter gehen und D wie folgt definieren:

alias -g D="|xargs rm"

Jetzt können Sie Folgendes eingeben:

cat file X1 D

um alle in der ersten Spalte der Datei "Datei" genannten Dateien zu löschen.

Wenn Sie die Bash kennen, ist das zsh bis auf einige neue Funktionen keine große Änderung.

HTH Chris

Chris
quelle
ahh, ich sehe, dass Sie Ihre Antwort aktualisiert haben, während ich meinen Kommentar oben eingegeben habe :) Können Sie irgendwie dynamisch angeben, welche Spalte abgerufen werden soll, oder würde ich n-Zeilen in meiner .zshrc benötigen (nicht, dass das wichtig ist, nur neugierig)
Sv1
Ich habe meinen Beitrag weiter bearbeitet und ein Suffix "D" definiert, um Dateien zu löschen. Soweit ich weiß, müssen Sie für jedes Suffix eine Zeile hinzufügen.
Chris
Oder machen Sie es zum ersten Parameter des X-Befehls. Nichts davon erfordert zsh oder Aliase, außer vielleicht für die eigentümliche Idee, eine Pipe in einen Alias ​​einzufügen.
Tripleee
1
Ich verstehe nicht, warum Ihnen allen die cutOption zu gefallen scheint . Es funktioniert einfach nicht auf meinem Computer mit der Ausgabe von svn st. Versuche svn st | cut -d' ' -f2nur zu sehen, was passiert.
Lynch
@ Sv1 Sie könnten eine Schleife zum Definieren der Aliase schreiben, da Alias ​​nur ein Befehl ist.
Driftcatcher
8

Hier ist ein Beispiel, da Sie mit Skripten nicht vertraut zu sein scheinen.

#!/bin/sh
# usage: svn st | x 2 | xargs rm
col=$1
shift
awk -v col="$col" '{print $col}' "${@--}"

Wenn Sie dies in speichern ~/bin/xund sicherstellen, dass ~/bines in Ihrem ist PATH(jetzt ist das etwas, was Sie in Ihr setzen können und sollten.bashrc Sie jetzt ), haben Sie den kürzestmöglichen Befehl zum allgemeinen Extrahieren der Spalte n; x n.

Das Skript sollte eine ordnungsgemäße Fehlerprüfung und Kaution durchführen, wenn es mit einem nicht numerischen Argument oder der falschen Anzahl von Argumenten usw. aufgerufen wird. Die Erweiterung dieser unverzichtbaren Version wird jedoch in Einheit 102 erfolgen.

Vielleicht möchten Sie das Skript erweitern, um einen anderen Spaltenbegrenzer zuzulassen. Awk analysiert standardmäßig die Eingabe in Felder im Leerzeichen. Um ein anderes Trennzeichen zu verwenden, verwenden Sie -F ':'wo :ist das neue Trennzeichen. Wenn Sie dies als Option für das Skript implementieren, wird es etwas länger, daher überlasse ich dies dem Leser als Übung.


Verwendung

Gegeben eine Datei file:

1 2 3
4 5 6

Sie können es entweder über stdin übergeben (indem Sie einen Nutzlosencat lediglich als Platzhalter für etwas Nützlicheres verwenden);

$ cat file | sh script.sh 2
2
5

Oder geben Sie es als Argument für das Skript an:

$ sh script.sh 2 file
2
5

Hierbei sh script.shwird davon ausgegangen, dass das Skript wie script.shim aktuellen Verzeichnis gespeichert ist . Wenn Sie es mit einem nützlicheren Namen irgendwo in Ihrem speichern PATHund es als ausführbar markieren, wie in den obigen Anweisungen beschrieben, verwenden Sie stattdessen offensichtlich den nützlichen Namen (und nein sh).

Tripleee
quelle
Gute Idee, funktioniert aber bei mir nicht ganz so wie bei sh oder bash. Dies funktioniert: #! / Bin / bash col = $ 1 Schicht awk "{print \ $$ col}"
mgk
Danke für diesen späten Kommentar. Mit Ihrem Fix aktualisiert.
Tripleee
1
besserawk -v col=$col ...
fedorqui 'SO hör auf,'
@fedorqui Danke für dein Feedback hier und anderswo! Die Antwort wurde aktualisiert. Es ist jetzt etwas länger so , wenn Sie die absolut kürzeste Skript mögen , können Sie haben, die sehen die Versionshistorie für die Originalversion.
Tripleee
1
@ Fedorqui Vielen Dank! Aus cathysterischen Gründen wurde dem Beispiel eine kleine Einschränkung hinzugefügt (-:
Tripleee
5

Sie haben anscheinend bereits eine Lösung. Um die Sache zu vereinfachen, setzen Sie Ihren Befehl einfach in ein Bash-Skript (mit einem kurzen Namen) und führen Sie diesen aus, anstatt jedes Mal diesen 'langen' Befehl einzugeben.

Tarek Fadel
quelle
Es ist nur so, dass es meiner Meinung nach nicht so "cool" ist. Warum? Nun, ich möchte meine .bashrc nicht mit verschiedenen Verknüpfungen überfluten müssen, die dies und das tun und im Wesentlichen eine Meta-Shell schreiben. Es ist ziemlich voreingenommen, wie es ist. Trotzdem ist Ihr Vorschlag nicht schlecht.
Sv1
2
.bashrc? Warum sollten Sie es mit nicht-Bash-bezogenen Dingen überladen? Ich schreibe einzelne Skripte für jeden Befehlssatz und lege sie alle in ein ~/scriptsVerzeichnis. Fügen Sie dann das Ganze ~/scriptszu meinem hinzu, PATHdamit ich sie einfach beim Namen nennen kann, wenn ich sie brauche.
Tarek Fadel
4
Dann legen Sie es nicht in Ihre .bashrc. Erstellen Sie ein Skript in ~ / bin und stellen Sie sicher, dass es sich auf Ihrem Pfad befindet.
Tripleee
2

Wenn Sie die Spalte manuell auswählen können, können Sie mit pick :

svn st | pick | xargs rm

Gehen Sie einfach zu einer beliebigen Zelle der 2. Spalte, drücken Sie cund drücken Sie dannenter

Bernardo Rufino
quelle
Ich habe das "Auswahl" -Tool ausprobiert, auf das Sie verwiesen haben, und es gefällt mir sehr gut. Es ist sehr gut für viele Situationen, in denen ich ausgewählte Spalten drucken möchte, aber nicht "awk '{print $ 3}'" eingeben möchte, um die gewünschte Spalte zu erhalten. Leider widerspricht der Name einem anderen "Pick" -Tool, das populärer zu sein scheint - es kann mit "apt install pick" installiert werden. Deshalb habe ich Ihr Tool in "pickk" auf meinem System umbenannt und beabsichtige, es weiterhin zu verwenden. Vielen Dank für die Veröffentlichung einer Referenz.
William Dye
1

Beachten Sie, dass sich der Dateipfad nicht in der zweiten Spalte der svn st-Ausgabe befinden muss. Wenn Sie beispielsweise die Datei ändern und ihre Eigenschaft ändern, ist dies die 3. Spalte.

Mögliche Ausgabebeispiele finden Sie in:

svn help st

Beispielausgabe:

 M     wc/bar.c
A  +   wc/qax.c

Ich schlage vor, die ersten 8 Zeichen zu schneiden durch:

svn st | cut -c8- | while read FILE; do echo whatever with "$FILE"; done

Wenn Sie 100% sicher sein möchten und sich beispielsweise mit ausgefallenen Dateinamen mit Leerzeichen am Ende befassen möchten, müssen Sie die XML-Ausgabe analysieren:

svn st --xml | grep -o 'path=".*"' | sed 's/^path="//; s/"$//'

Natürlich möchten Sie möglicherweise einen echten XML-Parser anstelle von grep / sed verwenden.

Michał Šrajer
quelle