Bash dla każdego

Większość języków programowania ma jakoś zaimplementowane pętle for. Bash nie jest tu wyjątkiem, chociaż składnia jest po Bashowemu trochę udziwniona. Najpierw podam najprostszy przykład sprawdzający wielkość plików w katalogu:

for plik in `ls /home/lukasz/katalog` ; do du -h $plik ; done

Najpierw wymyślamy nazwę zmiennej, do której będziemy podstawiać kolejne wartości podawane przez ls, a potem wykonujemy du -h na każdej jednej z tych wartości. Sztuczka polega na tym, że lista ma być po prostu ciągami znaków oddzielonymi spacją, lub znakiem nowej linii. Może to być więc lista domen zapisana w pliku:

for linia in `cat domeny.txt` ; do echo $linia ; host $linia ; echo '------------------' ; done

Inny przykład: oglądanie plików hosts.

for i in `cat hosty` ; do echo $i; ssh -o PreferredAuthentications=publickey -o PasswordAuthentication=no -l lukasz $i "cat /etc/resolv.conf" ; echo '------------------' ;
done

Dzięki PrefferedAuthentications oraz PasswordAuthentication unikniemy zapytań o hasło i, nawet jeżeli nasza lista hostów będzie bardzo długa, możemy wrócić do komputera po minucie i sktypt zakończy działanie, informując gdzie nie mógł logować się po kluczu.

Bash i zawijanie linii

Bash ma historię wpisywanych w terminal rzeczy. To bardzo dobrze. Używając strzałek w górę oraz w dół można przeglądać ostatnio wpisywane rzeczy. To również bardzo dobrze. W czym więc problem? Skąd właściwie bash wie, gdzie kończy się nasz prompt, a zaczyna historia? Skąd wie, gdzie umieścić znaki polecenia, które przekracza jedną linię?

Ano może sobie policzyć na której kolumnie terminala kończy się prompt, a na której zaczyna moje pisanie. Problem jest taki, że Bash liczy sobie znaki niedrukowane, na przykład kody kolorów, czy inne tego typu rzeczy. Oczywiście rachunek wychodzi błędnie, ponieważ ilość znaków w PS1 nie równa się tym wypisywanym na terminalu. Wynikiem tego są takie kwiatki:

[ lukasz@GAZEvim .bashrc

Jak temu zaradzić? Obszerne wyjaśnienie problemu jest na stronie Stack Exchange. Ja podam tylko wybrane przeze mnie rozwiązanie, polegające na wyciągnięciu niedrukowanych znaków do zmiennych i opatrzeniu ich numerkami:

green="\001$(tput setaf 2)\002"
blue="\001$(tput setaf 4)\002"
dim="\001$(tput dim)\002"
reset="\001$(tput sgr0)\002"
PS1="$green\u@\h $blue $ \n  $green->$reset  "
export PS1
unset green blue dim reset

Ło matko, co to jest? W zmiennych z kolorami oraz zmiennej resetującej kolor tłumaczymy Bashowi jak krowie na polu, że $(tput setaf 2) ma liczyć jako jeden znak. Czy te zmienne są niezbędne? Nie, ale bez nich czytelność linijki PS1 byłaby niewielka. Nazwy tych zmiennych oraz kolory ustawiane setafem nie mają oczywiście znaczenia. Dzięki nim unikamy znaków niedrukowanych w prompcie i bash wie, gdzie zacząć moje pisanie.