Mam ciąg zawierający wiele słów z co najmniej jedną spacją między nimi. Jak mogę podzielić ciąg na poszczególne słowa, aby móc je przewijać?
Ciąg jest przekazywany jako argument. Np ${2} == "cat cat file"
. Jak mogę przez to przejść?
Jak mogę sprawdzić, czy ciąg znaków zawiera spacje?
Odpowiedzi:
Czy próbowałeś po prostu przekazać zmienną łańcuchową do
for
pętli? Na przykład Bash automatycznie podzieli się na białe znaki.źródło
A=${A}${word})
.touch NOPE; var='* a *'; for a in $var; do echo "[$a]"; done
wyjścia[NOPE] [a] [NOPE]
zamiast oczekiwanych[*] [a] [*]
(LF zastąpione przez SPC dla czytelności).Podoba mi się konwersja na tablicę, aby mieć dostęp do poszczególnych elementów:
teraz możesz uzyskać bezpośredni dostęp do poszczególnych elementów (zaczyna się od 0):
lub przekonwertować z powrotem na ciąg, aby wykonać pętlę:
Oczywiście już wcześniej odpowiedziano bezpośrednio na pętlę ciągu, ale ta wada miała tę wadę, że nie śledziła poszczególnych elementów do późniejszego użycia:
Zobacz także Odwołanie do tablicy Bash .
źródło
touch NOPE; var='* a *'; arr=($var); set | grep ^arr=
wynikiarr=([0]="NOPE" [1]="a" [2]="NOPE")
zamiast oczekiwanycharr=([0]="*" [1]="a" [2]="*")
Wystarczy użyć wbudowanego „zestawu” powłok. Na przykład,
Następnie poszczególne słowa w tekście $ będą w 1 $, 2 $, 3 $ itd. Aby uzyskać solidność, zwykle robi się
aby obsłużyć przypadek, w którym $ text jest pusty lub rozpocząć od myślnika. Na przykład:
To drukuje
źródło
awk
aleset
jest o wiele łatwiejsze. Jestem terazset
fanboyem. Dzięki @Idelic!touch NOPE; var='* a *'; set -- $var; for a; do echo "[$a]"; done
wyniki[NOPE] [a] [NOPE]
zamiast oczekiwanych[*] [a] [*]
. Używaj go tylko wtedy, gdy masz 101% pewności, że w podzielonym ciągu nie ma metaznaków SHELL!set -f
przedset -- $var
iset +f
po nim wyłączyć globowanie.set -f
rozwiązania jest bezpieczny, zbyt. Aleset +f
jest domyślną wartością każdej powłoki, więc jest to istotny szczegół, który należy zauważyć, ponieważ inni prawdopodobnie nie są tego świadomi (tak jak ja też).Prawdopodobnie najłatwiejszym i najbezpieczniejszym sposobem w BASH 3 i nowszych jest:
(gdzie
arr
jest tablica, która pobiera podzielone części łańcucha) lub, jeśli na wejściu mogą znajdować się znaki nowej linii i potrzebujesz więcej niż tylko pierwszego wiersza:(zwróć uwagę na miejsce w środku
-d ''
, nie można go zostawić), ale może to dać nieoczekiwany znak nowej linii<<<"$var"
(ponieważ domyślnie dodaje to LF na końcu).Przykład:
Wyprowadza oczekiwane
ponieważ to rozwiązanie (w przeciwieństwie do wszystkich poprzednich rozwiązań tutaj) nie jest podatne na nieoczekiwane i często niekontrolowane globowanie powłoki.
Daje to również pełną moc IFS, jak zapewne chcesz:
Przykład:
Wyprowadza coś takiego:
Jak widać, spacje można również zachować w ten sposób:
wyjścia
Należy pamiętać, że obsługa
IFS
w BASH jest przedmiotem sama w sobie, podobnie jak testy, kilka interesujących tematów na ten temat:unset IFS
: Ignoruje przebiegi SPC, TAB, NL oraz on-line start i endIFS=''
: Bez separacji pola, wszystko czytaIFS=' '
: Uruchamia SPC (i tylko SPC)Ostatni przykład
wyjścia
podczas
wyjścia
BTW:
Jeśli nie jesteś
$'ANSI-ESCAPED-STRING'
przyzwyczajony do tego, oznacza to oszczędność czasu.Jeśli nie podasz
-r
(jak wread -a arr <<<"$var"
), wtedy read unika odwrotnego ukośnika. Pozostaje to jako ćwiczenie dla czytelnika.W przypadku drugiego pytania:
Aby przetestować coś w ciągu, zwykle trzymam się tego
case
, ponieważ może to sprawdzić wiele przypadków jednocześnie (uwaga: case wykonuje tylko pierwsze dopasowanie, jeśli potrzebujesz przewrotnego użyciacase
instrukcji mnożenia ), a taka potrzeba jest dość często (pun zamierzony):Możesz więc ustawić wartość zwracaną w celu sprawdzenia SPC w następujący sposób:
Dlaczego
case
? Ponieważ zwykle jest nieco bardziej czytelny niż sekwencje wyrażeń regularnych, a dzięki metaznakom Shell bardzo dobrze radzi sobie z 99% wszystkich potrzeb.źródło
set -f
lubset -o noglob
przełączać globowanie, tak aby metaznaki powłoki nie wyrządzały więcej szkody w tym kontekście. Ale tak naprawdę nie jestem tego przyjacielem, ponieważ pozostawia to za sobą wiele mocy powłoki / jest bardzo podatne na błędy, aby przełączać się tam iz powrotem.;&
osiągnąć. Nie jestem pewien, w której wersji bash się pojawił. Jestem użytkownikiem 4.3;&
jest wymuszone przewijanie bez sprawdzania wzorca, jak w C. I jest też taki,;;&
który kontynuuje dalsze sprawdzanie wzorca. Więc;;
jest jakif ..; then ..; else if ..
i;;&
jest jakif ..; then ..; fi; if ..
, gdzie;&
jestm=false; if ..; then ..; m=:; fi; if $m || ..; then ..
- nigdy nie przestaje się uczyć (od innych););;&
zanim skomentowałeś: D Dzięki, i niech skorupa będzie z tobą;)Aby sprawdzić spacje, użyj grep:
źródło
echo "X" |
można zwykle zastąpiony przez<<<"X"
coś takiego:grep -s " " <<<"This contains SPC"
. Możesz zauważyć różnicę, jeśli zrobisz cośecho X | read var
w przeciwieństwie doread var <<< X
. Tylko ta ostatnia importuje zmiennąvar
do bieżącej powłoki, a aby uzyskać do niej dostęp w pierwszym wariancie, musisz pogrupować w następujący sposób:echo X | { read var; handle "$var"; }
(A) Aby podzielić zdanie na jego słowa (oddzielone spacją), możesz po prostu użyć domyślnego IFS, używając
Przykład uruchomienia następującego fragmentu kodu
wyjdzie
Jak widać, możesz bez problemu używać pojedynczych lub podwójnych cudzysłowów.
Uwagi:
- jest to w zasadzie to samo co odpowiedź moba , ale w ten sposób przechowujesz tablicę na wszelkie dalsze potrzeby. Jeśli potrzebujesz tylko jednej pętli, możesz użyć jego odpowiedzi, która jest krótsza o jedną linię :)
- zapoznaj się z tym pytaniem, aby uzyskać alternatywne metody dzielenia łańcucha na podstawie ogranicznika.
(B) Aby sprawdzić znak w ciągu, możesz również użyć dopasowania wyrażenia regularnego.
Przykład sprawdzenia obecności znaku spacji, którego możesz użyć:
źródło
Aby sprawdzić spacje tylko za pomocą bash:
źródło
Powoduje to wyświetlenie każdego słowa, które możesz przetworzyć na liście według własnego uznania.
źródło