Jak odzyskać pierwsze słowo wyniku polecenia w bash?


127

Mam polecenia, na przykład: echo "word1 word2". Chcę wstawić potok ( |) i uzyskać słowo1 z polecenia.

echo "word1 word2" | ....

Nie wiem, co położyć za fajką.

Odpowiedzi:


201

Awk to dobra opcja, jeśli masz do czynienia z końcowymi białymi spacjami, ponieważ zajmie się tym za Ciebie:

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

Cut nie zajmie się tym jednak:

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

„Wytnij” tutaj nie drukuje nic / białych znaków, ponieważ pierwszą rzeczą przed spacją była kolejna spacja.


Czy średnik jest konieczny?
Alice Purcell,

1
Powinien być „wiodącym” odstępem (na początku ciągu), a nie „końcowym”.
user202729

@AlicePurcell Wypróbowałem to bez; i u mnie zadziałało (MBP 10.14.2)
Samy Bencherif

1
To nie działa, jeśli ciąg to np. „FirstWord, secondWord”, ponieważ to polecenie awk ogranicza spacje
Roger Oba

@RogerOba To nie było pytanie OP, ale możesz użyć -F","do zastąpienia domyślnego separatora pól (spacji) przecinkiem.
pjd

70

nie ma potrzeby używania poleceń zewnętrznych. Sam Bash może wykonać zadanie. Zakładając, że „słowo1 słowo2” skądś pobrałeś i zapisałeś w zmiennej np

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

teraz możesz przypisać $ 1 lub $ 2 itd. do innej zmiennej, jeśli chcesz.


11
+1 za używanie tylko wbudowanych powłok i stdin. @Matt M. --oznacza stdin, że $stringjest przekazywany jako stdin. stdinjest oddzielone białymi znakami na argumenty $1, $2, $3, itd. - tak jak wtedy, gdy bash wartościowanie programu argumenty (np czek $1, $2itd), to podejście wykorzystuje skłonność powłoki o podziale stdinautomatycznie do argumentów, usuwając potrzebę awklub cut.
Caleb Xu,

3
@CalebXu Nie stdin, setustawia argumenty powłoki.
Guido

9
word1=$(IFS=" " ; set -- $string ; echo $1)Ustaw IFS, aby poprawnie rozpoznawał odstępy między wyrazami. Zawiń w nawiasy, aby uniknąć wybicia oryginalnej zawartości 1 USD.
Steve Pitchers

Jest to zepsute, ponieważ podlega rozszerzaniu nazw plików. Wypróbuj z string="*". Niespodzianka.
gniourf_gniourf

32

Myślę, że skutecznym sposobem jest użycie tablic bash:

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

Możesz także bezpośrednio czytać tablice w rurociągu:

echo "word1 word2" | while read -a array; do echo "${array[0]}" ; done

1
echo " word1 word2 " | { read -a array ; echo ${array[0]} ; }
Boontawee Home

Jest to zepsute, ponieważ podlega rozszerzaniu nazw plików. Wypróbuj z string="*". Niespodzianka.
gniourf_gniourf

Użyj whileskładni, aby pobrać każde pierwsze słowo w każdym wierszu. W przeciwnym razie użyj podejścia Boontawee Home. Należy również pamiętać, że echo "${array[0]}"zostało zacytowane, aby zapobiec ekspansji, jak zauważył gniourf-gniourf.
Isaías,

Jeśli spróbujesz uzyskać dostęp do indeksu tablicy, który jest większy niż liczba słów, nie otrzymasz błędu. Otrzymasz po prostu pustą linię
Dhumil Agarwal

26
echo "word1 word2 word3" | { read first rest ; echo $first ; }

Ma to tę zaletę, że nie używa zewnętrznych poleceń i pozostawia zmienne $ 1, $ 2 itd. Nienaruszone.


Pozostawienie zmiennych $1, $2, …nienaruszonych jest niezwykle przydatną funkcją przy pisaniu skryptów!
Serge Stroobandt

15

Jeśli jesteś pewien, że nie ma spacji wiodących, możesz użyć podstawiania parametrów bash:

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

Uważaj na ucieczkę z pojedynczej przestrzeni. Zobacz tutaj, aby uzyskać więcej przykładów wzorców zastępowania. Jeśli masz bash> 3.0, możesz również użyć dopasowywania wyrażeń regularnych, aby poradzić sobie ze spacjami wiodącymi - zobacz tutaj :

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

11

Możesz spróbować awk

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

W awk bardzo łatwo jest wybrać dowolne słowo (1 $, 2 $, ...)


11

Korzystanie z rozszerzania parametrów powłoki %% *

Oto inne rozwiązanie wykorzystujące rozszerzenie parametrów powłoki . Dba o wiele spacji po pierwszym słowie. Obsługa spacji przed pierwszym słowem wymaga jednego dodatkowego rozszerzenia.

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

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

Wyjaśnienie

W %%oznacza usuwanie najdłuższy mecz  *(spację dowolnej liczby dowolnych innych znaków) w tylnej części string.


9

Zastanawiałem się, jak kilka najpopularniejszych odpowiedzi wypada pod względem szybkości. Przetestowałem:

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]} ; }

i 4 @ boontawee-home's

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

Zmierzyłem je za pomocą czasu Pythona w skrypcie Bash w terminalu Zsh na macOS, używając ciągu testowego z 215 5-literowymi słowami. Wykonałem każdy pomiar pięć razy (wszystkie wyniki obejmowały 100 pętli, najlepszy z 3) i uśredniono wyniki:

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")

Dobra robota, wyborcy 👏 Głosy (w chwili pisania tego tekstu) odpowiadają szybkości rozwiązań!


Dziwne, że można zmierzyć 3 w myślniku, ponieważ myślnik nie obsługuje tablic ( read -ajest nieprawidłowy w myślniku).
gniourf_gniourf

Tak, to dziwne. Wykluczyłem to, zrobiłem testy szybkości, po czym pomyślałem „dlaczego zostawiłem to” i dodałem. Usuwam to teraz i mogę powtórzyć to później, aby upewnić się, że nie popełniłem błędu
henry

6
echo "word1 word2" | cut -f 1 -d " "

cut wycina pierwsze pole (-f 1) z listy pól rozdzielonych ciągiem znaków "" (-d "")


to jeden sposób, ale twoja instrukcja cut nie rozróżni wielu spacji między słowami, jeśli chce uzyskać słowo 2 później
ghostdog74

tak, rozwiązanie awk jest lepsze.
lajuette

3

read jest twoim przyjacielem:

  • Jeśli ciąg jest w zmiennej:

    string="word1 word2"
    read -r first _ <<< "$string"
    printf '%s\n' "$first"
  • Jeśli pracujesz w potoku: pierwszy przypadek: chcesz tylko pierwszego słowa z pierwszego wiersza:

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

    drugi przypadek: chcesz, aby pierwsze słowo w każdym wierszu:

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

Działają, jeśli istnieją spacje wiodące:

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

0

Ponieważ perl zawiera funkcjonalność awk, można to również rozwiązać za pomocą perla:

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

0

Pracowałem z urządzeniem wbudowanym, które nie miało ani Perla, awk, ani Pythona i zamiast tego zrobiłem to z sedem. Obsługuje wiele spacji przed pierwszym słowem (których rozwiązania cuti bashnie obsłużyły).

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

Było to bardzo przydatne podczas grepowania psdla identyfikatorów procesów, ponieważ inne rozwiązania tutaj używające tylko basha nie były w stanie usunąć pierwszych spacji psużywanych do wyrównania.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.