Automatyczny help w skryptach bashowych

Opatrzenie skryptu tekstem pomocy to wielkie ułatwienie dla użytkownika, który nie wie jak tego skryptu użyć, a czasem i dla… samego twórcy, który już nie pamięta szczegółów budowy swojego programu.
autohelp

Kiedy wraca się do własnego skryptu napisanego kilka miesięcy wcześniej często nie pamięta się już ani jak on działa, ani jakich można użyć argumentów. Pozostaje wtedy otwarcie go w edytorze i przeanalizowanie. Dobrze, jeśli kod został skomentowany, a najlepiej byłoby mieć „help” – tekst pomocy, w którym opisane jest działanie skryptu i możliwe do zastosowania argumenty. Zwykle szkoda jest na to czasu – skrypty pisane są po to, by przyspieszyć pracę, a nie żeby jej sobie dokładać. Ale gdyby tak ten „help” robił się… sam?

Chciałbym zaproponować rozwiązanie, które bardzo mi pomogło przy pracy ze skryptami bashowymi. Jest proste i pozwala łatwo przygotować tekst pomocy dla użytkownika bez zbędnego nakładu pracy.

Swego czasu pisałem dużo skrytpów bashowych. Były różne – od prostych, wykonujących jedną czynność po kilkusetwierszowe, skomplikowane, z wieloma argumentami. Często okazywało się, że jeśli nie używałem ich kilka miesięcy, miałem problem z ich składnią, argumentami itp. Na szczęście mam zwyczaj komentowania kodu więc podgląd w edytorze pozwalał sobie przypomnieć jak działa skrypt i jakich użyć argumentów. Nie jest to jednak najwygodniejsze wyjście, wymaga otwarcia edytora i czasochłonnego szukania w kodzie komentarzy. Wtedy pojawiła się koncepcja autohelpa.

Czym jest autohelp

„Autohelp” – bo jest tworzony automatycznie.

„Autohelp” – bo skrypt w pewnym sensie sam siebie komentuje.

Koncepcja jest następująca: pisząc kod skryptu opatruje się go krótkimi (ale czytelnymi) komentarzami. Po wywołaniu skryptu z argumentem -h lub --help (co kto lubi) komentarze te są wyświetlane w postaci tekstu pomocy. Wadą rozwiązania jest to, że te komentarze wciąż trzeba wpisać. Nikt tego za programistę nie zrobi. Zaletą – że komentarze pozostają tam, gdzie są najbardziej potrzebne czyli w odpowiednich miejscach kodu, a jednocześnie tworzą zwarty tekst pomocy, który jest łatwo dostępny i czytelny bardziej niż teksty porozrzucane w wielu miejscach.

Komentarze

Autohelp musi mieć co wyświetlić więc kod skryptu należy opatrzyć komentarzami. Komentarze w skryptach bashowych są oznaczane znakiem # na początku linii. Na potrzeby autohelpa te z nich, które mają się pojawić w tekście pomocy oznacza się dwoma znakami ## i spacją:

## to jest tekst pomocy

Powyższy tekst zostanie wyświetlony w tekście pomocy.

##to jest komentarz
# to jest komentarz

Te teksty nie zostaną wyświetlone w tekście pomocy. Takie rozróżnienie pozwala na pozostawienie w kodzie zwykłych komentarzy (np. dotyczących zawartości zmiennych, todo itp.), które w tekście pomocy nie są potrzebne.

Parsowanie argumentów

Wywołanie tekstu pomocy odbywa się poprzez uruchomienie skryptu z argumentem -h lub --help. Wymaga to przetwarzania (parsowania) argumentów. Jeśli więc w skrypcie ma być zawarty autohelp to konieczne jest przetwarzanie argumentów nawet jeśli w samym skrypcie nie jest to potrzebne. Jest kilka sposobów, żeby to zrobić, na potrzeby autohelpa wybrałem pętlę z instrukcją case. Jest to rozwiązanie uniwersalne, pozwala używać argumentów zarówno w krótkiej formie (-h), jak i w długiej (--help), a nawet argumentów z podaną wartością. Ponieważ pętla przechodzi po wszystkich argumentach, odrzucając je kolejno, została zamknięta w funcji, dzięki czemu skrypt ma w dalszym ciągu dostęp do wszystkich podanych argumentów poprzez $1, $2 itd. Do funkcji jest przekazywany cały dostępny zestaw argumentów ($@). Cała konstrukcja wygląda następująco:

 # funkcja, w której parsowane są argumenty
 function aparse(){
     # pętla pobierająca kolejne argumenty skryptu
     while [ ! -z $1 ]
     do
         case "$1" in
             ## -u | --upper – ustawia wielkie litery
                -u|--upper)  UPPER=true
                         ;;
             ## -w | --width SZER - określa docelową szerokość tekstu w znakach 
                -w|--width)  WIDTH=$2;
                             shift;
                         ;;
         esac
         # następny argument
        shift
     done
 }
 aparse $@ 

Szczegóły instrukcji case można sobie przypomnieć np. z doskonałego podręcznika Advanced Bash-Scripting Guide.

W powyższym przykładzie widoczna jest funcja aparse, wewnątrz której jest pętla while, pobierająca kolejne argumenty skryptu. Za przechodzenie od argumentu do argumentu odpowiedzialna jest instrukcja shift, która usuwa pierwszy argument i przesuwa kolejne – tak więc $2 staje się $1 i tak dalej.

Rozpoznawane argumenty są kolejnymi opcjami instrukcji case. Jak widać można podać równocześnie krótką i długą formę, oddzielając je znakiem | – obie zadziałają.

Dla przykładu podałem jako drugi argument z wartością. Ponieważ aktualnie przetwarzany argument jest na pierwszej pozycji to wartość znajduje się na drugiej, może więc być odczytana jako $2. Trzeba tylko pamiętać, żeby ją dodatkowo „przeskoczyć”, dodając shift na końcu polecenia – jak to jest widoczne w przykładzie.

Obie opcje zostały skomentowane już w takim formacie, w jakim spodziewa się tego autohelp: dwa znaki # i spacja. Jak widać zostały umieszczone w miejscach, do których się odnoszą, stanowiąc opis następujących po nich linii.

Parsowanie komentarzy

Jak więc z tych elementów zbudować tekst pomocy?

Trzeba pamiętać, że skrypt bashowy jest plikiem tekstowym i jako taki daje się przeszukiwać za pomocą polecenia grep. A mając do dyspozycji wyróżnik w postaci ciągu „## ” łatwo już wyciągnąć potrzebne nam linie komentarza z kodu skrytpu. W moim rozwiązaniu polecenie wygląda następująco:

cat "$0" | grep "^\s*## " | sed 's/^\s*## / /'

Pierwsza część to wypisanie kodu skryptu, który następnie jest – w drugiej części – filtrowany pod kątem ciągu wyróżniającego tekst pomocy. Trzecia część usuwa początkowe ##, bo w tekście pomocy wyświetlanym w konsoli brzydko to wygląda.

Szablon

Wypisywanie tego wszystkiego za każdym razem mijałoby się z celem – rozwiązanie ma przyspieszać pracę, a nie dawać jej dodatkowy narzut. Rzecz jasna najlepiej jest przygotować sobie szablon i zaczynać budowę skryptu od takiego modułu, który załatwia dwie sprawy od razu – parsowanie argumentów i szybkie tworzenie tekstu pomocy.

Oto gotowy szablon, którego ja sam używam:

 #!/bin/bash
 
 #Na początek informacja co robi skrypt i jaka jest składnia
 
 ## Skrypt testowy
 ## składnia: helptest [argumenty]

 function aparse(){
     while [ ! -z $1 ]
     do
         case "$1" in
             -h|--help)  HELPTEXT=$(cat "$0" | grep "^\s*## " | sed 's/^\s*## / /')
             echo -e "${HELPTEXT}"
             exit 0
             ;;
              
             ## -u | --upper – ustawia wielkie litery
             -u|--upper)  UPPER=true
             ;;
 
             ## -w | --width SZER - określa docelową szerokość tekstu w znakach 
             -w|--width)  WIDTH=$2;
             shift;
             ;;
         esac
         shift
     done
 }
 aparse $@
 

 

Zapisany w pliku pod nazwą helptest i uruchomiony z argumentem --help daje następujący wynik:

 
 $ helptest –help
 
  Skrypt testowy
  użycie: helptest [argumenty]
  -u | --upper – ustawia wielkie litery
  -w | --width SZER - określa docelową szerokość tekstu w znakach 

Szablon zawiera dwie opcje – jedną zwykłą, drugą z wartością. W mojej pracy najczęściej któraś mi się przydaje więc od razu są na miejscu, wystarczy je odpowiednio zmodyfikować. Szablon można zapisać w pliku np. w katalogu Szablony i mieć go zawsze pod ręką (a raczej pod prawym klawiszem myszy) bądź wpisać sobie do snippetów w ulubionym edytorze i wklejać szybko do nowo tworzonych skryptów.

Pomysł ten został sprawdzony w praktyce i zdał egzamin. Tworzyłem pakiet obejmujący prawie setkę skryptów dla kilkuosobowego zespołu. Pisanie odrębnych tekstów pomocy zajęłoby dużo czasu, dzięki temu rozwiązaniu taki tekst powstawał praktycznie w czasie pisania skryptu (zawsze komentuję kod, tym razem robiłem to tylko w specyficzny sposób). Polecam wypróbowanie tego sposobu – jeśli komuś się spodoba i wdroży go u siebie, będzie mi miło, że mój pomysł komuś pomógł w pracy.

1 komentarz
Dodaj komentarz

Twój adres email nie zostanie opublikowany. Wymagane pola są oznaczone *

Poprzedni post

CyberOS – Nowość na jądrze Arch

Następny post

toplip – ciekawy program do szyfrowania

Powiązane posty