Z tym pierwszym skryptem, jako centralnym tematem niniejszego wpisu, to trochę – jak się okaże w trakcie lektury – taka clickbaitowa przesada. W gruncie rzeczy dzisiaj będzie przede wszystkim mowa o przygotowań do tego, by móc nasz skrypt w ogóle odpalić.
Czym jest skrypt?
De facto każdy skrypt – nie tylko bash – jest niczym więcej niż zwykłym plikiem tekstowym. Tym niemniej pewne cechy takiego pliku powodują, że w “sprzyjających okolicznościach” za jego pomocą możemy osiągnąć nierzadko spektakularne efekty, choć w przypadku skryptów bash na ogół chodzi o zaoszczędzenie sobie czasu, który – w przeciwnym razie – musielibyśmy poświęcić na żmudne klepanie w terminalu kolejnych poleceń.
Skoro tak, to trzeba w tym miejscu spróbować odpowiedź sobie na pytanie, co mianowicie składa się na te “sprzyjające okoliczności”? W przypadku skryptów bash warunki są dwa. Pierwszy z nich jest dość oczywisty – otóż taki skrypt musi zawierać listę instrukcji zrozumiałych dla powłoki, które ta ostatnie spróbuje następnie wykonać. W dzisiejszym tekściem temu zagadnieniu nie poświęcimy za wiele miejsca, choć w gruncie rzeczy jest to clou całej tej serii wpisów. Zamiast tego przyjrzymy się drugiemu warunków, które mam na myśli, a mianowicie temu, co sprawia, że powłoka w ogóle wie, że ma do czynienia ze skryptem bash, a nie zwykłym plikiem tekstowym albo skryptem innego języka.
W przypadku linuksa czy szerzej dla systemów uniksowych taką wskazówką nie jest bynajmniej rozszerzenie pliku stosowane w Okienkach. Wszelkie rozszerzenia typu “exe”, “bin” czy “sh” z punktu widzenia powłoki nie są niczym więcej niż częścią nazwy tego konkretnego pliku, a już na pewno nie są interpretowane jako wskaźnik typu. Tym niemniej nie zmienia to faktu, że autorzy skryptów bash zwyczajowo dodają na końcu jego nazwy końcówkę “.sh”, ale w pierwszym rzędzie chodzi tutaj o oflagowanie na potrzeby własne lub dla innych użytkowników, którzy na podstawie takiego “przyrostka” mogą wstępnie założyć, że w tym przypadku mamy rzeczywiście do czynienia ze skryptem bash, a nie na ten przykład Pythona albo ze zwykłym plikiem tekstowym. Ewentualnie robi się tak również ze względu na edytory tekstowe, które na podstawie takiego zakończenia od startu “kolorują” odpowiednio zawartość takiego pliku (a przynajmniej tak robią te “lepsze”, choć nie jest to jedyny wskaźnik na te potrzeby używany, o czym będzie dalej).
Oczywiście możemy w przypadku takiego pliku – o ile wiemy, że mamy do czynienia ze skryptem bash – wskazywać wprost w terminalu, by został on “uruchomiony” przy pomocy powłoki bash. Wówczas wygląda to mniej więcej tak:
/bin/bash nazwa_skryptu.sh
Tym niemniej nie jest to sposób powszechnie praktykowany, ponieważ można to samo osiągnąć znacznie prościej, choć w takim przypadku trzeba na początku zadać sobie odrobinę trudu.
“Prawidłowa” obsługa skryptów
Otóż w takim przypadku trzeba zacząć od przypisania takiemu plikowi odpowiednich uprawnień. Do tego celu służy w Linuksie polecenie chmod, które – jak to w unixach bywa – można wykorzystać na kilka sposobów. Niestety szczegółowe omówienie tej kwestii wymagałoby przybliżenia zagadnienia uprawnień w Linuksie, co jednak nadaje się na osobny wpis, a już na pewno nie bardzo da się to opowiedzieć w kilku słowach, choć próbuję nieco przybliżyć temat w ramce niżej.
Pamiętaj proszę, że to co napisałem niżej jest pewnym uproszczeniem!
W przypadku linuksów czy też szerzej systemów uniksowych każdy plik oraz folder opisany jest trzema podstawowymi uprawnieniami:
- prawo do odczytu,
- prawo do zapisu (w tym usunięcia),
- prawo do uruchomienia.
Ten podział skomplikowany jest przez fakt, że uprawnienia te są implementowane na trzech poziomach. Mamy więc właściciela, grupę oraz pozostałych użytkowników systemu. Pomijam przy tym użytkownika root (superuser), który może niemal wszystko i wszędzie. Żeby to było bardziej zrozumiałe, spróbuję omówić to na przykładzie folderu “Pobrane” w moim katalogu domowym. Widoczny niżej wpis otrzymałem przy odpowiednim użyciu polecenia ls.
drwxr-xr-x. 3 borciugner borciugner 4096 11-07 15:49 Pobrane
Literka “d” na samym początku mówi, że w tym przypadku mamy do czynienia z katalogiem, a nie plikiem chociażby. Natomiast z perspektywy omówienia kwestii nas interesującej ważne są literki i myślniki, które widać dalej. Są one pogrupowane po trzy, pierwsza triada dotyczy właściciela (tutaj borciugner), kolejna uprawnień na poziomie przypisanej grupy do tego folderu (tutaj grupa to również borciugner – zbieżność nazw nie powinna nas mylić, ponieważ to są dwie odrębne rzeczywistości), zaś ostatnia pozostałych użytkowników. Same litery oznaczają odpowiednio:
- r – prawa odczytu (read),
- w – prawa zapisu (write),
- x – prawa do uruchamiania/wykonywania (execute).
W tym przypadku jedynie właściciel ma pełnię praw (rwx), ponieważ zarówno członkowie przypisanej grupy, jak i inni użytkownicy nie mają prawa zapisu oraz wykonywania (r-x).
W naszym przypadku chodzi o nadanie uprawnień do wykonywania/uruchamiania takich skryptów. Jak było to wspomniane wyżej, polecenie chmod pozwala to ćwiczenie wykonać na kilka sposób, ale osobiście preferuję to z wskazaniem ciągu 3 cyfr, co może wygląda mniej więcej tak:
chmod 755 nazwa_skryptu.sh
Kombinacja tych cyfr może być różna w zależności od celu, który chcemy osiągnąć tym sposobem. Natomiast w tym konkretnych przypadku nadaliśmy uprawnienia dla wszystkich użytkowników systemu nie tylko do uruchamiania, ale też odczytu naszego skryptu. Jednakże przy tym nikt poza autorem nie może majstrować przy jego zawartości, czyli zapisywać zmian w pliku czy jego nazwie.
Dodam tylko na wszelki wypadek, że w wielu miejscach zamiast cyferek można zobaczyć najczęściej coś takiego:
chmod +x nazwa_skryptu.sh
Dodatkowo w większości popularnych środowisk graficznych – o ile działamy na Linuksie w jedno z nich uzbrojonym – przynajmniej do pewnego stopnia można to samo wyklikać używając “gryzonia”, ale osobie, która zamierza zostać mistrzem wiersza poleceń, tak nie wypada.
Od tego momentu wystarczy w terminalu wpisać nazwę takiego skryptu oraz ścieżkę dostępu do pliku. Natomiast jeśli znajdujemy się w tym samym katalogu, co nasze skrypt wystarczy wklepać:
./nazwa_skryptu.sh
Co by nie było wątpliwości dodam, że przed nazwą skryptu dodajemy kropkę i backslash, co jest równoznaczne z poinformowaniem powłoki, że chcemy odpalić plik znajdujący się w katalogu, w którym sami obecnie przebywamy.
Istnieje jeszcze jedna opcja, w przypadku której możemy całkiem zrezygnować z konieczności podawania ścieżki dostępu do skryptu, czyli po prostu wpisywać jego nazwę. By skorzystać z takiego udogodnienia musimy zadbać, by nasz plik znalazł się w jednym z katalogów, na które wskazuje zmienna środowiskowa PATH. O zmiennych będzie więcej w kolejnym wpisie, tutaj jedynie napiszę, że akurat ta zmienna trzyma informację o tych miejscach, które powłoka przeszukuje na okoliczność programów do uruchomienia.
By zweryfikować, o jakie miejsca w tym wypadku chodzi wystarczy wpisać w terminalu
echo $PATH
Nie będę na razie opowiadał o szczegółach tego, co przed momentem zrobiliśmy. Ważne, że w naszym terminalu powinniśmy wówczas zobaczyć coś takiego:
Tym sposobem “wylistowana” została lista takich lokalizacji (miejsce separatora spełnia tutaj dwukropek). W przypadku różnych dystrybucji może oczywiście się ta zawartość nieco różnić, ale zwracam uwagę na widoczną tam ścieżkę “/home/borciugner/bin”. W przypadku Fedory ten wpis pojawia się z automatu, co nie oznacza, że taki katalog rzeczywiście istnieje. Natomiast wówczas wystarczy stworzyć w naszym domowym folderze odpowiedni katalog przy pomocy polecenia:
mkdir /home/borciugner/bin
Od tego momentu każdy skrypt, który tam umieścimy będziemy mogli uruchomić wpisując w wierszu poleceń niezależnie od tego, gdzie jesteśmy obecnie zlokalizowani.
W przypadku innych dystrybucji może być konieczność odpowiedniej modyfikacji zmiennej PATH. Oczywiście nic – poza uprawnieniami – nie stoi na przeszkodzie, by własną twórczość wrzucać chociażby do katalogu /usr/bin, ale wydaje mi się, że rozwiązania z dedykowanym folderem w domowym katalogu jest bardziej praktyczna opcją.
By zmodyfikować zawartość tej zmiennej należy wklepać w terminal następujące polecenie:
export PATH=”$HOME/bin:$PATH”
Oczywiście jeśli ktoś woli to zamiast wywołania zmiennej HOME (kolejna zmiana środowiska) może wpisać pełną ścieżkę, czyli /home/borciugner/bin na początku cudzysłowiu zamiast fragmentu “$HOME”. Po prostu “$HOME” zastępuje domyślny dostęp do naszego katalogu domowego (można w miejsce $ HOME wpisać symbol tyldy “~”, ale nie chcę już bardziej mieszać).
I to właściwie wszystko, co warto wiedzieć na temat uruchamiania skryptów.
Pierwszy skrypt
Jak to wszystko? – może zapytać ktoś zaznajomiony z tematem. Brakuje wszak informacji na temat roli “shebang” w skryptach. W pewnym sensie takie osoby mają rację, ale tylko częściową. Albo inaczej sprawę stawiając: przeceniają znaczenie tego elementu skryptu, które jest przecież opcjonalny. Tym niemniej na sam koniec poświęcę jeszcze dwa słowa tej kwestii, przy okazji omawiając zwyczajową strukturę skryptów bash. Zrobimy to na przykładzie obiecanego w tytule “pierwszego skryptu”, który wygląda tak:
#!/bin/bash echo "Hello World!" exit 0
Właściwa część skryptu w tym przypadku zaczyna się od polecenia echo i generalnie jest to jedyny obligatoryjny element tego skryptu, ponieważ bez instrukcji do wykonania skrypt de facto nie byłby skryptem.
Zagadnieniu wychodzenia z programu (skryptu) reprezentowanemu przez znajdującą się na końcu instrukcję exit poświęcimy więcej uwagi przy okazji omawiania instrukcji warunkowych. W przypadku tak prostego skryptu można byłoby sobie spokojnie darować użycie tego polecenia, ale osobiście uważam, że to dobry zwyczaj, by na końcu każdego skryptu mimo wszystko dodawać instrukcję exit z wartością zero, ale o tym w swoim czasie.
Tym sposobem przechodzimy do “kontrowersyjnej” kwestii poruszonej na początku tej części wpisu. Otóż pierwszy wiersz określany właśnie jako “shebang” jest informacją dla powłoki, że do uruchomienia tego pliku należy użyć “programu” bash. Czytając literalnie można w związku z tym zastanawiać się nad moim lekceważącym podejściem dla tej kwestii. Od razu jednak zaznaczę, że bynajmniej zagadnienia tego nie bagatelizuję i w moich skryptach zawsze zaczynam od dodanie tego “komentarz” w pierwszy wierszu. Natomiast ważne jest, by mieć świadomość, że jeśli tego nie zrobimy, to powłoka bash sobie poradzi i taki skrypt uruchomi.
Wpisywanie tego polecenia jest nieodzowne tylko w bardzo specyficznych sytuacjach, czyli wówczas, gdy powłoka bash nie jest domyślną dla systemu, z którego korzystamy, a nasz skrypt był pisany pod konkretnie pod tę powłokę. Tak się bowiem składa, że jakkolwiek bash jest zdecydowanie najpopularniejszą powłoką (przynajmniej w przypadku linuksów), to wcale nie musi być on powłoką domyślną, ot chociażby ze względu na decyzje twórców dystrybucji lub stosowne modyfikacje administratora tej instancji OS. Jakkolwiek inne powłoki są – tak przynajmniej wyczytałem w wielu miejscach – są niemal w 100% kompatybilne z bash, wcale tak być nie musi w przypadku naszego skryptu, który z powodu pewnych różnich może się wówczas nie wykonać w prawidłowy sposób.
Gdyby ktoś chciał się upewnić jaka powłoka jest domyślna w przypadku jego systemy wystarczy wywołać przy pomocy polecenia echo zmianę SHELL:
echo “$SHELL”
Wówczas w zasadzie niemal zawsze zobaczymy /bin/bash, czyli tę właśnie ścieżkę, którą wskazujemy w przypadku shebang.
Koniec
Jak zwykle miał to być krótki tekst o pewnych podstawowych kwestiach, ale standardowo nie wyszło. Przyrzekam jednak, że kolejne wpisy będę już bardziej treściwe, a wszelkie dygresje postaram wydzielić z tekstu, tak jak to miało dzisiaj miejsce przy omawianiu uprawnień. Zaś w kolejnej odsłonie tej serii będzie mowa o zmiennych, co w przypadku basha jest dość ciekawym zagadnieniem.