Wie kann man das erste Wort der Ausgabe eines Befehls in bash abrufen?

126

Ich habe zum Beispiel einen Befehl : echo "word1 word2". Ich möchte eine Pipe ( |) setzen und word1 vom Befehl erhalten.

echo "word1 word2" | ....

Ich weiß nicht, was ich nach der Pfeife setzen soll.

Neuquino
quelle

Antworten:

201

Awk ist eine gute Option, wenn Sie sich mit nachgestellten Leerzeichen befassen müssen, da dies für Sie erledigt wird:

echo "   word1  word2 " | awk '{print $1;}' # Prints "word1"

Cut kümmert sich aber nicht darum:

echo "  word1  word2 " | cut -f 1 -d " " # Prints nothing/whitespace

'cut' hier druckt nichts / Leerzeichen, weil das erste, was vor einem Leerzeichen stand, ein anderes Leerzeichen war.

mattbh
quelle
Ist das Semikolon notwendig?
Alice Purcell
1
Es sollte ein "führendes" Leerzeichen (am Anfang der Zeichenfolge) und kein "nachfolgendes" Leerzeichen sein.
user202729
@ AlicePurcell Ich habe es ohne versucht; und es hat bei mir funktioniert (MBP 10.14.2)
Samy Bencherif
1
Dies funktioniert nicht, wenn die Zeichenfolge zB "firstWord, secondWord" ist, da dieser awk-Befehl durch Leerzeichen begrenzt wird
Roger Oba
@RogerOba Das war nicht die Frage des OP, aber Sie können -F","das Standardfeldtrennzeichen (ein Leerzeichen) mit einem Komma überschreiben.
27.
70

Es müssen keine externen Befehle verwendet werden. Bash selbst kann den Job machen. Angenommen, "word1 word2" stammt von irgendwoher und ist in einer Variablen gespeichert, z

$ string="word1 word2"
$ set -- $string
$ echo $1
word1
$ echo $2
word2

Jetzt können Sie einer anderen Variablen $ 1 oder $ 2 usw. zuweisen, wenn Sie möchten.

Ghostdog74
quelle
11
+1 für die Verwendung nur eingebauter Shell und stdin. @Matt M. --bedeutet stdin, wird also $stringals übergeben stdin. stdindurch Leerzeichen getrennt in Argumente $1, $2, $3usw. - so wie wenn ein heftiger Schlag Programm auswertet Argumente (zB Scheck $1, $2etc.), ist dieser Ansatz den Vorteil der Tendenz der Shell nimmt die aufzuspalten stdinautomatisch in Argumente, für die Beseitigung der Notwendigkeit awkoder cut.
Caleb Xu
3
@CalebXu Nicht stdin, setlegt die Shell-Argumente fest.
Guido
9
word1=$(IFS=" " ; set -- $string ; echo $1)Stellen Sie IFS so ein, dass der Abstand zwischen den Wörtern korrekt erkannt wird. In Klammern setzen, um zu vermeiden, dass der ursprüngliche Inhalt von 1 US-Dollar beeinträchtigt wird.
Steve Pitchers
Dies ist fehlerhaft, da es einer Pfadnamenerweiterung unterliegt. Probieren Sie es mit string="*". Überraschung.
gniourf_gniourf
32

Ich denke, ein effizienter Weg ist die Verwendung von Bash-Arrays:

array=( $string ) # do not use quotes in order to allow word expansion
echo ${array[0]}  # You can retrieve any word. Index runs from 0 to length-1

Sie können Arrays auch direkt in einer Pipeline lesen:

echo "word1 word2" | while read -a array; do echo "${array[0]}" ; done
Isaías
quelle
1
echo " word1 word2 " | { read -a array ; echo ${array[0]} ; }
Boontawee Home
Dies ist fehlerhaft, da es einer Pfadnamenerweiterung unterliegt. Probieren Sie es mit string="*". Überraschung.
gniourf_gniourf
Verwenden Sie die whileSyntax, um jedes erste Wort in jeder Zeile abzurufen. Verwenden Sie andernfalls den Boontawee Home-Ansatz. Bitte beachten Sie auch, dass echo "${array[0]}"dies zitiert wurde, um eine von gniourf-gniourf festgestellte Erweiterung zu verhindern.
Isaías
Wenn Sie versuchen, auf einen Index des Arrays zuzugreifen, der größer als die Anzahl der Wörter ist, wird keine Fehlermeldung angezeigt. Sie erhalten nur eine leere Zeile
Dhumil Agarwal
26
echo "word1 word2 word3" | { read first rest ; echo $first ; }

Dies hat den Vorteil, dass keine externen Befehle verwendet werden und die Variablen $ 1, $ 2 usw. intakt bleiben.

John Marter
quelle
Das Beibehalten der Variablen $1, $2, …ist eine äußerst nützliche Funktion zum Schreiben von Skripten!
Serge Stroobandt
14

Wenn Sie sicher sind, dass keine führenden Leerzeichen vorhanden sind, können Sie die Bash-Parameterersetzung verwenden:

$ string="word1  word2"
$ echo ${string/%\ */}
word1

Achten Sie darauf, dass Sie dem einzelnen Raum entkommen. Sehen Sie hier für weitere Beispiele von Substitutionsmuster. Wenn Sie bash> 3.0 haben, können Sie auch die Übereinstimmung regulärer Ausdrücke verwenden, um mit führenden Leerzeichen umzugehen - siehe hier :

$ string="  word1   word2"
$ [[ ${string} =~ \ *([^\ ]*) ]]
$ echo ${BASH_REMATCH[1]}
word1
dsl101
quelle
11

Sie könnten awk versuchen

echo "word1 word2" | awk '{ print $1 }'

Mit awk ist es wirklich einfach, ein beliebiges Wort auszuwählen ($ 1, $ 2, ...)

mfloryan
quelle
11

Shell-Parametererweiterung verwenden %% *

Hier ist eine andere Lösung mit Shell-Parametererweiterung . Es kümmert sich um mehrere Leerzeichen nach dem ersten Wort. Die Behandlung von Leerzeichen vor dem ersten Wort erfordert eine zusätzliche Erweiterung.

string='word1    word2'
echo ${string%% *}
word1

string='word1    word2      '
echo ${string%% *}
word1

Erläuterung

Die %%bedeutet das Löschen die längste mögliche Übereinstimmung von  *(einem Raum , gefolgt von einer beliebigen Anzahl von was auch immer andere Zeichen) im hinteren Teil string.

Serge Stroobandt
quelle
9

Ich fragte mich, wie viele der Top-Antworten in Bezug auf die Geschwindigkeit gemessen wurden. Ich habe folgendes getestet:

1 @ mattbh's

echo "..." | awk '{print $1;}'

2 @ ghostdog74's

string="..."; set -- $string; echo $1

3 @ boontawee-home's

echo "..." | { read -a array ; echo ${array[0]} ; }

und 4 @ boontawee-home's

echo "..." | { read first _ ; echo $first ; }

Ich habe sie mit Pythons Zeit in einem Bash-Skript in einem Zsh-Terminal unter macOS gemessen, wobei ich eine Testzeichenfolge mit 215 Wörtern aus 5 Buchstaben verwendet habe. Hat jede Messung fünfmal durchgeführt (die Ergebnisse waren alle für 100 Schleifen, am besten von 3) und hat die Ergebnisse gemittelt:

method       time
--------------------------------
1. awk       9.2ms
2. set       11.6ms (1.26 * "1")
3. read -a   11.7ms (1.27 * "1")
4. read      13.6ms (1.48 * "1")

Gute Arbeit, Wähler 👏 Die Stimmen (zum Zeitpunkt dieses Schreibens) entsprechen der Geschwindigkeit der Lösungen!

Henry
quelle
Seltsam, dass Sie 3 in Bindestrich messen könnten, da Bindestrich keine Arrays unterstützt ( read -aist in Bindestrich ungültig).
gniourf_gniourf
Ja das ist komisch. Ich schloss das aus, machte die Geschwindigkeitstests, dachte dann "Warum habe ich das weggelassen" und fügte es hinzu. Wenn ich es jetzt entferne, kann ich die Dinge später erneut ausführen, um sicherzustellen, dass ich keinen Fehler hatte
Henry
6
echo "word1 word2" | cut -f 1 -d " "

cut schneidet das 1. Feld (-f 1) aus einer Liste von Feldern ab, die durch die Zeichenfolge "" (-d "") begrenzt sind.

Lajuette
quelle
Das ist eine Möglichkeit, aber Ihre Cut-Anweisung unterscheidet nicht mehrere Leerzeichen zwischen Wörtern, wenn er später
word2
Ja, die awk-Lösung ist die bessere.
Lajuette
3

read ist dein Freund:

  • Wenn sich die Zeichenfolge in einer Variablen befindet:

    string="word1 word2"
    read -r first _ <<< "$string"
    printf '%s\n' "$first"
  • Wenn Sie in einer Pipe arbeiten: Erster Fall: Sie möchten nur das erste Wort der ersten Zeile:

    printf '%s\n' "word1 word2" "line2" | { read -r first _; printf '%s\n' "$first"; }

    Zweiter Fall: Sie möchten das erste Wort jeder Zeile:

    printf '%s\n' "word1 word2" "worda wordb" | while read -r first _; do printf '%s\n' "$first"; done

Diese funktionieren, wenn es führende Räume gibt:

printf '%s\n' "   word1 word2" | { read -r first _; printf '%s\n' "$first"; }
gniourf_gniourf
quelle
0

Da Perl die Funktionalität von awk enthält, kann dies auch mit Perl gelöst werden:

echo " word1 word2" | perl -lane 'print $F[0]'
tssch
quelle
0

Ich habe mit einem eingebetteten Gerät gearbeitet, das weder Perl, Awk noch Python hatte, und es stattdessen mit sed gemacht. Es werden mehrere Leerzeichen vor dem ersten Wort unterstützt (die mit den cutund bash-Lösungen nicht behandelt wurden).

VARIABLE="  first_word_with_spaces_before_and_after  another_word  "
echo $VARIABLE | sed 's/ *\([^ ]*\).*/\1/'

Dies war sehr nützlich, wenn psnach Prozess-IDs gesucht wurde , da die anderen Lösungen, die hier nur bash verwenden, die ersten Leerzeichen, die pszum Ausrichten verwendet werden, nicht entfernen konnten .

Johan Bjäreholt
quelle