Ten artykuł został napisany bardzo dawno temu. Istnieje duże prawdopodobieństwo, że od tego czasu autor zmienił swoje poglądy!

Bash - przydatne konstrukcje

Ostatnio 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 mana 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.

Źródła