Bash - przydatne konstrukcje
Thu 16 July 2015 #technical #programming #bashOstatnio stwierdziłem, że dobrze by było odświeżyć porządnie swoją wiedzę z zakresu pisania skryptów w powłoce tekstowej. Stwierdziłem, że ilość przydatnych informacji, które znalazłem jest dostatecznie duża, żeby napisać na ten temat posta lub może zacząć jakąś serię postów na ten temat. Nie ma lepszego sposobu na nauczenie się nowych rzeczy niż przez uczenie na ich temat innych (a w przypadku bloga pewnie siebie za jakiś czas). Do dzieła!
Bashowy strict mode?
Dość często porównuje się różne języki skryptowe i muszę przyznać, że perlowa zasada dodawania
use strict;
use warnings;
do dłuższych skryptów jest bardzo sensownym pomysłem, bo zaoszczędza masę czasu na debugowanie niedziałających skryptów. I okazuje się, że w Bashu przy odrobinie kreatywności jesteśmy osiągnąć podobny efekt. Wystarczy, żeby na początku skryptu dodać takie linijki :
set -euo pipefail
IFS=$'\n\t'
Nie jest to odpowiednik jeden do jeden perlowego zachowania, ale i tak jest to coś o czym powinien wiedzieć każdy kto chce nauczyć się pisać porządne skrypty systemowe. A teraz co dokładnie robią poszczególne opcje, które tutaj włączamy? (Zachęcam do przejrzenia odpowiedniej strony z podręcznika dotyczącego Basha) Po pierwsze można by to rozbić na poszczególne polecenia:
set -e
set -u
set -o pipefail
Flaga -e
Włączenie tej flagi powoduje, że każda komenda w skrypcie, która zwróci wartość inną niż zero spowoduje jego zatrzymanie. Zmienia to działanie interpretera na to, które jest bliższe działaniu innych języków skryptowych jak na przykład Python. Jest to opcja, której raczej nie włączymy wpisując polecenia "z palca" w terminal, ale jest niesamowicie przydatna, gdy chcemy napisać jakiś dłuższy skrypt.
Flaga -u
Dzięki niej odwołanie się do wcześnej niezadeklarowanej zmiennej jest
traktowane jako błąd i powoduje natychmiastowe zakończenie wykonywania
skryptu. Problemy związane z literówkami przy włączeniu tej opcji są
bardzo szybkie do wychwycenia i naprawienia. Jest to dość podobne do
skorzystania z use strict 'vars'
w Perlu. Naprawdę pozwala na
uniknięcie wielu niedogodności.
set -o pipefail
Flaga -o
pozwala na włączenie opcji za pomocą jej nazwy w tym wypadku
jest to pipefail
, która odkrywa wszelkie błędy, które mogą się pojawić
w potokach, a nie będą widoczne na pierwszy rzut oka. Posłużę się tutaj
przykładem ze źródła tej informacji.
$ grep jakis-tekst /nieistniejacy/plik | sort
grep: /nieistniejacy/plik: No such file or directory
$ echo $?
0
Jak widać w zmiennej $?
znajduje się wartość zwróconą przez sort
,
który pomimo, że dostał pusty string do posortowania traktuje go jako
poprawne dane wejściowe. Nie jest to zachowanie, które pasuje do
skryptów powłoki, ale bez wątpienia wystarcza przy normalnym pracowniu z
terminalem.
$ set -o pipefail
$ grep jakis-tekst /nieistniejacy/plik | sort
grep: /nieistniejacy/plik: No such file or directory
$ echo $?
2
Zmienna IFS
Zgodnie z tym co można znaleźć na stronach man
a dotyczących Basha:
IFS The Internal Field Separator that is used for word splitting after expansion and to split lines into words with the read builtin command. The default value is ``\ \t\n''.
Dlaczego go zmieniać? Pomaga to w zmianie sposobu w jaki Bash będzie iterować po listach zmiennych. Jest to dość pomocne szczególnie, gdy spojrzy się na skrypt, który mógłby dla przykładu robić coś z plikami, których nazwy podawałoby się przy wywołaniu.
#!/bin/bash
for arg in $@; do
echo "Removing $arg"
done
Bez zmiany IFS wywołanie tego skryptu z argumentami mogłoby skutkować pomyłką w ich interpretacji:
$ ./skrypt.sh test-file 'Mission Impossible.webm'
Removing test-file
Removing Mission
Removing Impossible.webm
Więcej informacji na temat działania Basha po takich zmianach można znaleźć w oryginalnym artykule, do którego link znajduje się na końcu tego posta.