Pętla przez linie pliku: wyjaśnienie pętli for w Bashu
Chcę zapętlić linie pliku w skrypcie Bash i jednym ze sposobów na zrobienie tego jest użycie pętli for.
Co to jest pętla for?
Pętla for jest jedną z najbardziej powszechnych konstrukcji programistycznych i jest używana do wykonywania danego bloku kodu na podstawie zestawu elementów na liście. Na przykład, powiedzmy, że chcesz napisać program, który wypisuje liczbę osób, które mieszkają w 10 największych miastach Europy. Program może używać pętli for do przechodzenia przez każde miasto na liście i drukowania liczby osób dla tego miasta.
Wykonywana logika jest za każdym razem taka sama, a jedyną rzeczą, która się zmienia, jest miasto.
Poniżej możesz zobaczyć ogólną składnię pętli Bash for:
for item in do command1 command2 ... commandNdone
LISTĄ może być na przykład:
- zakres liczb.
- sekwencją łańcuchów oddzielonych spacjami.
- wydaniem polecenia systemu Linux (np. polecenia ls).
N poleceń pomiędzy do i done jest wykonywanych dla każdego elementu na liście.
Pętla for w Bashu
W tym artykule dowiesz się, jak używać pętli for w Bashu, a konkretnie do przechodzenia przez linie pliku.
Ale dlaczego miałbyś to robić? Przechodzenie przez wiersze pliku?
Na przykład, możesz tego potrzebować, jeśli wyeksportowałeś dane z aplikacji do pliku i chcesz je jakoś opracować.
W tym przykładzie użyjemy prostego pliku .txt, w którym każdy wiersz zawiera:
- nazwę miasta
- liczbę osób, które mieszkają w tym mieście.
Poniżej możesz zobaczyć format pliku tekstowego, dwukropek jest używany do oddzielenia każdego miasta od liczby osób, które mieszkają w tym mieście:
Istanbul:15,067,724Moscow:12,615,279London:9,126,366...
Więc, jak możemy użyć pętli Bash for, aby przejść przez zawartość tego pliku?
Najpierw zapiszemy nazwę pliku w zmiennej
FILENAME="european-cities.txt"
Potem użyjemy innej zmiennej i polecenia cat, aby uzyskać wszystkie linie w pliku:
LINES=$(cat $FILENAME)
Tutaj używamy podstawiania poleceń, aby przypisać wyjście polecenia cat do zmiennych LINES.
W końcu pętla for pozwala przejść przez każdą linię pliku:
for LINE in $LINESdo echo "$LINE"done
Do i done są używane do definiowania poleceń, które mają być wykonywane przy każdej iteracji pętli for.
Na przykład, jeśli masz plik z 10 liniami, pętla for przejdzie przez 10 iteracji i przy każdej iteracji odczyta jedną linię pliku.
Komenda echo może być zastąpiona przez dowolną sekwencję komend w oparciu o to, co chcesz zrobić z każdą linią w pliku.
Tutaj jest końcowy skrypt:
#!/bin/bash FILENAME="european-cities.txt"LINES=$(cat $FILENAME)for LINE in $LINESdo echo "$LINE"done
A wyjście skryptu to…
./cities.sh Istanbul:15,067,724Moscow:12,615,279London:9,126,366Saint-Petersburg:5,383,890Berlin:3,748,148Kyiv:3,703,100Madrid:3,223,334Rome:2,857,321Paris:2,140,526Bucharest:2,106,144
Przekazujemy listę do pętli for używając komendy cat.
To oznacza, że możemy użyć dowolnych komend do wygenerowania LISTY, którą chcemy przekazać do pętli for.
Czy masz na myśli jakieś inne możliwe polecenia?
Pętla for nie jest jedyną opcją tworzenia pętli w skrypcie Bash, inną opcją jest pętla while.
Czym jest licznik w pętli for Bash?
W pętli for można również zdefiniować zmienną o nazwie licznik. Możesz użyć licznika do śledzenia każdej iteracji pętli.
Użycie licznika jest bardzo powszechne we wszystkich językach programowania. Można go również użyć do uzyskania dostępu do elementów struktury danych wewnątrz pętli (nie dotyczy to naszego przykładu).
Zmodyfikujmy poprzedni program i zdefiniujmy licznik, którego wartość jest wypisywana przy każdej iteracji:
#!/bin/bash FILENAME="european-cities.txt"LINES=$(cat $FILENAME)COUNTER=0for LINE in $LINESdo echo "Counter $COUNTER: $LINE" COUNTER=$((COUNTER+1))done
Jak widzisz, zdefiniowałem zmienną o nazwie COUNTER poza pętlą for z jej wartością początkową ustawioną na 0.
Następnie w każdej iteracji wypisuję wartość licznika wraz z linią z pliku.
Po wykonaniu tych czynności używam operatora arytmetycznego Bash, aby zwiększyć wartość zmiennej COUNTER o 1.
A oto wynik działania skryptu:
Counter 0: Istanbul:15,067,724Counter 1: Moscow:12,615,279Counter 2: London:9,126,366Counter 3: Saint-Petersburg:5,383,890Counter 4: Berlin:3,748,148Counter 5: Kyiv:3,703,100Counter 6: Madrid:3,223,334Counter 7: Rome:2,857,321Counter 8: Paris:2,140,526Counter 9: Bucharest:2,106,144
Break and Continue in a Bash For Loop
Istnieją sposoby na zmianę normalnego przepływu pętli for w Bashu.
Dwie instrukcje, które na to pozwalają, to break i continue:
- break: przerywa wykonywanie pętli for i przeskakuje do pierwszego wiersza po pętli for.
- continue: przeskakuje do następnej iteracji pętli for.
Zdefiniowanie licznika pomaga nam zobaczyć, co się stanie, gdy dodamy break lub continue do naszego istniejącego skryptu.
Zacznijmy od break…
Dodam instrukcję if opartą na wartości licznika. Instrukcja break wewnątrz instrukcji if przerywa wykonywanie pętli, jeśli licznik jest równy 3:
#!/bin/bash FILENAME="european-cities.txt"LINES=$(cat $FILENAME)COUNTER=0for LINE in $LINESdo if ; then break fi echo "Counter $COUNTER: $LINE" COUNTER=$((COUNTER+1))done
A wyjście to:
Counter 0: Istanbul:15,067,724Counter 1: Moscow:12,615,279Counter 2: London:9,126,366
Jak widać instrukcja break zatrzymuje wykonywanie pętli for przed osiągnięciem polecenia echo, ponieważ COUNTER jest równy 3.
Po tym, zamieńmy break na continue i zobaczmy co się stanie. Pozostawię resztę kodu bez zmian.
#!/bin/bash FILENAME="european-cities.txt"LINES=$(cat $FILENAME)COUNTER=0for LINE in $LINESdo if ; then continue fi echo "Counter $COUNTER: $LINE" COUNTER=$((COUNTER+1))done
A oto dane wyjściowe dla skryptu:
Counter 0: Istanbul:15,067,724Counter 1: Moscow:12,615,279Counter 2: London:9,126,366
Dziwne…dane wyjściowe są takie same. Dlaczego?
To dlatego, że kiedy wartość licznika COUNTER wynosi 3, instrukcja continue przeskakuje do następnej iteracji pętli, ale nie zwiększa wartości licznika.
Więc w następnej iteracji wartość licznika COUNTER nadal wynosi 3 i instrukcja continue jest wykonywana ponownie, i tak dalej dla wszystkich pozostałych iteracji.
Aby to naprawić musimy zwiększyć wartość zmiennej COUNTER wewnątrz instrukcji if:
#!/bin/bash FILENAME="european-cities.txt"LINES=$(cat $FILENAME)COUNTER=0for LINE in $LINESdo if ; then COUNTER=$((COUNTER+1)) continue fi echo "Counter $COUNTER: $LINE" COUNTER=$((COUNTER+1))done
Tym razem widzimy poprawne wyjście:
Counter 0: Istanbul:15,067,724Counter 1: Moscow:12,615,279Counter 2: London:9,126,366Counter 4: Berlin:3,748,148Counter 5: Kyiv:3,703,100Counter 6: Madrid:3,223,334Counter 7: Rome:2,857,321Counter 8: Paris:2,140,526Counter 9: Bucharest:2,106,144
Jak widać „Licznik 3: ….” nie jest drukowany w terminalu.
Pisanie pętli for w jednej linii
Przed zakończeniem tego tutoriala, zobaczmy jak możemy napisać pętlę for w jednej linii.
Nie jest to sugerowana praktyka, ponieważ sprawia, że twój kod jest mniej czytelny.
Ale dobrze jest wiedzieć jak napisać pętlę w jednej linii, to daje więcej głębi twojej wiedzy o Bashu.
Generyczna składnia pętli Bash for w jednym wierszu jest następująca:
for i in ; do ; done
Wypisujmy zawartość naszego pliku tekstowego za pomocą jednowierszowej pętli for:
#!/bin/bashFILENAME="european-cities.txt"LINES=$(cat $FILENAME)for LINE in $LINES; do echo $LINE; done
Aby uprościć sprawę, usunąłem licznik COUNTER i instrukcję if. Gdyby tam były, jednowierszowa pętla for byłaby o wiele trudniejsza do przeczytania.
Postaraj się unikać jednowierszowych instrukcji, jeśli sprawiają, że twój kod jest trudny do przeczytania.
Podsumowanie
Podsumowując, w tym poradniku nauczyłeś się jak:
- Przechowywać linie pliku w zmiennej
- Używać pętli for do przechodzenia przez każdą linię.
- Używaj licznika w pętli for.
- Zmieniaj przepływ pętli za pomocą break i continue.
- Pisz pętlę for w jednym wierszu.
Ma to sens?
Jak zamierzasz tego użyć?
Daj znać 🙂
Jeśli chcesz dowiedzieć się więcej o pętlach w skryptach Bash zajrzyj do tego tutoriala.
Powiązany DARMOWY KURS: Decipher Bash Scripting
.