Blog

Bash dla Hobbystów. Zmienne - finalizacja

Dość obszerna glosa do ostatniego wpisu na temat zmiennych.

Bash dla Hobbystów. Zmienne - finalizacja

Dzisiaj mała - mam taką nadzieję - glosa do ostatniego wpisu o bash. Skupi się ona na 3 obszarach, czyli:

Polecenia jako wartości

To jeden z ważniejszych aspektów zmiennych, ponieważ gros problemów, które chcemy zaadresować przy pomocy skryptów, wymaga posługiwania się rezultatami wykonania tego czy owego polecenia lub programu. To truizm, ale warto to powtórzyć: w praktyce skrypty bash są niczym innym niż ciągiem poleceń powłoki, które są odpowiednio pospinane przy pomocy różnych konstrukcji programistycznych, by uprościć i/lub zautomatyzować pewne czynności, których realizacja z wiersza poleceń byłaby niezmiernie uciążliwa lub zwyczajnie nużąca. Dlatego też wiele razy przy tworzeniu takiego zlepka poleceń pojawia się potrzeba przypisania wyniku tego czy owego polecenia do zmiennej, by następnie wykorzystać tą wartość w innym miejscu. W przypadku klepania w terminalu zamiast tego moglibyśmy po prostu skopiować czy też zapisać dany rezultat i użyć go w swoim czasie.

By wykonać takie przypisanie potrzeba jednak czegoś więcej niż nazwy zmiennej, znaku równości oraz zapisania polecenia po prawej stronie tej “równości”. Musimy bowiem jednoznacznie wskazać interpreterowi, że oto w tym momencie ma wykonać dodatkowe kroki, zanim ustanowi wartość dla tej konkretnej zmiennej. Do tego celu na szczęście niewiele potrzeba, jednak - jak to w przypadku basha bywa - jest oczywiście więcej niż jedno podejście do tego zagadnienia. Po pierwsze, możemy użyć symbolu odwrotnego apostrofu, czyli symbolu, który znajduje się (powinien się znajdować) po lewej stronie klawiatury nad klawiszem tabulatora lub jak ktoś woli: z lewej strony od klawisza jedynki. Drugi sposób podobny do wywołania wartości zmiennej i wygląda następująco: $(polecenie).

Poniżej przykład zastosowania obu “metod”:

#!/bin/bash

 

zmienna1=`pwd`

zmienna2=$(ls -a | wc -l)

 

echo "To jest wartość pierwszej zmiennej: $zmienna1"

echo "To jest wartość pierwszej zmiennej: $zmienna2"

 

exit 0


Wybór którejkolwiek z nich jest głównie kwestią gustu, choć nie do końca, ponieważ sposób z użyciem symbolu dolara i nawiasów jest nie tylko nowszy, ale też zgodny - z tego co wyczytałem - ze standardami POSIX. W mojej skromnej opinii jest też zdecydowanie bardziej czytelny.

Argumenty jako zmienne

W zasadzie to temat na osobny wpis i być może kiedyś takowy popełnię, ale dzisiaj będzie wersja “kieszonkowa” tego zagadnienia.

Otóż w przypadku skryptów - podobnie jak dla poleceń - możemy używać argumentów przy ich wywołaniu. W gruncie rzeczy nic poza naszą wyobraźnią nie ogranicza nas w tym zakresie i możemy podać zapewne dowolną liczbę takich parametrów i wszystkie one zostaną przekazane do skryptu. Tym niemniej kwestia tego jak ta wiedza zostanie wykorzystana zależy wyłącznie od autora kodu. W większości wypadków zostanie zapewne zignorowana i w żaden sposób nie wpłynie na działanie “programu”, warto jednak wiedzieć jakie są sposoby na ich wykorzystanie, gdybyśmy widzieli taką potrzebę.

Otóż każdy taki argument trafia do skryptu i jest przypisywany do kolejnej zmiennej wg klucza:

$1 - wartość pierwszego argumentu

$2 - wartość drugiego argumentu

...

$N - wartość n-go argumentu.

Dodatkowo są dostępne jeszcze dwie specjalne zmienne na tę okoliczność przewidziane, czy: $@ i $#. Pierwsza z nich “zawiera” w sobie wszystkie przekazane argumenty, a druga podaje ich liczbę. Dla lepszego zobrazowania proponowałbym puścić poniższy skrypt z argumentami “Ala”, “ma” oraz “kota”(./skrypt.sh Ala ma kota)

#!/bin/bash

 

echo $1

echo $2

echo $3

echo "Liczba słów w zdaniu: $#"

echo $@


Jest to bardzo przydatne rozwiązanie, jeśli skrypt wymaga pewnego zaangażowania od osoby, która ten skrypt uruchamia. Tym niemniej są nieco lepsze sposoby na taką interaktywność - oczywiście “lepsze” w tym sensie, że nie pozostawiają niczego w gestii użytkownika, od którego nie oczekuje się wówczas, że będzie miał dokładną wiedzę w jaki sposób należy dany skrypt uruchomić, czyli jakie argumenty należy od razu przekazać. Zamiast tego można w trakcie wykonywania skryptu poprosić użytkownika o pewne dodatkowe informacje. Oczywiście wybór ścieżki postępowania przy tworzeniu skryptu zależy od konkretnego zastosowania, a przede wszystkim grona osób, które będą się nim posługiwały, ale do tego też z pewnością kiedyś wrócimy.

Jak nie liczyć w bash

Bezpośrednio w powłoce da się liczyć, choć niekoniecznie należy to robić, jeśli nie wymaga tego od nas logika skryptu, a w pewnych przypadkach wręcz nie należy. Dzieje się tak, ponieważ Bash nie został do tych celów pomyślany i zoptymalizowany, choć oczywiście udostępnia podstawowe działania arytmetyczne.

Operator

Opis

+

-

*

/

**

%

Dodawanie

Odejmowanie

Mnożenie

Dzielenie

Potęgowanie

Modulo, czyli reszta z dzielenia

Tym niemniej obce jest mu pojęcia liczb zmiennoprzecinkowych, więc przy próbie podzielenia liczby 5 przez 2 wynik może nas zaskoczyć. Żeby się o tym przekonać wystarczy w konsoli wpisać:

echo $((5/2))

Przy tej okazji dowiedzieliśmy się jak zapisywać działania arytmetyczne. Jak widać rozwiązanie jest podobne do tego z pierwszego punktu (dla wywoływania poleceń), tylko w miejsce pojedynczych nawiasów potrzebne są podwójne. Oczywiście - jak się zapewne domyśla uważny czytelnik - nie jest to jedyny sposób zapisu, bo w miejsce podwójnych nawiasów zwykłych, można użyć pojedynczych kwadratowych.

Na zakończenie

Podsumowując więc wszystko co dotychczas napisane w poniższej tabelce jest “ściągawka” dotycząca tego jak należy podchodzić do przypisywania wartości do zmiennych.

Przykład

Opis

z1=a



z2=’Ale ma 5$’



z3=”Cześć $USER”



z4=$(ls -a | grep wc -l)


z5=$((2+2))

Wartość łańcuch jednoelementowego - można wówczas pominąć ujmowania go w cudzysłów.


Użycie apostrofów dla złożonych łańcuchów w celu zapewnienia dosłownego odwzorowania wartości


Użycie cudzysłowu w celu wykorzystania mechanizmu podstawień


Przypisanie wyniku polecenia do zmiennej


Przypisanie wyniku działania arytmetycznego

W kolejnym wpisie będzie już nieco ciekawiej, bowiem przyjrzymy się instrukcjom warunkowym, które w przypadku bash mają swoje smaczki albo jak ktoś woli: dziwactwa.