#### {% title "Skryptologia stosowana" %} # Skryptologia stosowana
{%= image_tag "/images/borenstein.jpg", :alt => "[Nathaniel S. Borenstein]" %}W podręczniku użytkownika programu *bash* znajdziesz wszystkie szczegóły dotyczące programowania powłoki. Pomoc na temat wbudowanych poleceń uzyskasz, korzystając z polecenia *help*, na przykład: :::shell-unix-generic help if help case help help ## Zmienne powłoki specjalnego przeznaczeniaIt should be noted that no ethically-trained software engineer would ever consent to write a destroy_baghdad procedure. Basic professional ethics would instead require him to write a destroy_city procedure, to which Baghdad could be given as a parameter.
Zmienna | Znaczenie |
---|---|
$0 |
nazwa pliku ze skryptem |
$1 – $9 |
parametry pozycyjne przekazywane do skryptu |
${10} |
parametr 10. |
$# |
liczba parametrów przekazanych do skryptu |
"$*" |
wszystkie parametry (jedno słowo) |
"$@" |
wszystkie parametry (oddzielne słowa) |
${#*} |
liczba parametrów przekazanych do skryptu |
${#@} |
liczba parametrów przekazanych do skryptu |
$? |
wartość zwrócona przez skrypt |
$$ |
ID procesu (PID) |
Program *listing.sh* pokazuje jak za pomocą *ANSI escape sequences* kolorować wypisywany tekst: :::shell-unix-generic #!/bin/bash red='\e[31m' end_color='\e[0m' echo -e "${red}Listing katalogu:${end_color}" `pwd` ls --color -lt exit 0 A ten skrypt pokazuje jak zmieniać kolor tła i znaki normalne na znaki pobgrubione: :::shell-unix-generic #!/bin/bash esc="\033[" echo -n " _ _ _ _ _40 _ _ _ 41_ _ _ _42 _ _ _ 43" echo "_ _ _ 44_ _ _ _45 _ _ _ 46_ _ _ _47 _" for fore in 30 31 32 33 34 35 36 37; do line1="$fore " line2=" " for back in 40 41 42 43 44 45 46 47; do line1="${line1}${esc}${back};${fore}m Normal ${esc}0m" line2="${line2}${esc}${back};${fore};1m Bold ${esc}0m" done echo -e "$line1\n$line2" done ## Blank rename Czasami w nazwach plików pojawiają się spacje. To jest *bad thing*. Skrypt *blank-rename.sh* zamienia spacje w nazwach na znak podkreślenia *_*. :::shell-unix-generic #!/bin/bash number=0 # licznik plików, którym zmieniono nazwy FOUND=0 # zmienna: aby kod się lepiej czytał for filename in * # przejrzyj wszystkie pliki w katalogu do echo "$filename" | grep -q " " # sprawdź czy nazwa pliku if [ $? -eq $FOUND ] # zawiera spacje then fname=$filename # tak, więc zabieramy sie do pracy n=`echo $fname | sed -e "s/ /_/g"` # podstawiamy _ za każdą spację mv "$fname" "$n" # zmieniamy nazwę pliku let "number += 1" fi done echo "Liczba plików, którym zmieniono nazwy: $number" exit 0S.E.S.J.A. = System Eliminacji Studentów Już Aktywny
## *shift* – przetwarzanie opcji Polecenie `shift` jest używane do manipulowania argumentami pozycyjnymi. Na przykład po wywołaniu `shift` parametr `$1` otrzymuje dotychczasową wartość parametru `$2`, parametr `$2` — wartość parametru `$3` itd. Każde wywołanie `shift` zmniejsza wartość `$#`. Polecenie przyjmuje opcjonalny argument określający rozmiar przesunięcia; domyślna wartość to 1. Poniższy skrypt pokazuje jak wykorzystać `shift` do przetwarzania opcji: `-f`, `-v` i `-q`. :::shell-unix-generic #!/bin/bash file= verbose= quiet= while [ $# -gt 0 ] do case $1 in -f) file=$2 shift # dlaczego? ;; -v) verbose=true quiet= ;; -q) quiet=true verbose= ;; --) shift # para kreseczek oznacza zwyczajowo koniec opcji break ;; -*) echo $0: $1: nieznana opcja >&2 ;; *) break # argument nie będący opcją; przerwanie przetwarzania opcji ;; esac shift # przesuń argumenty dla następnego przebiegu pętli done Ale zwykle tak nie programujemy. Upraszczamy sobie zadanie przetwarzania opcji korzystając z polecenia wbudowanego `getopts`. ## Szaradzista Skrypt *szaradzista.sh* dopasowuje wzorzec zadany wyrażeniem regularnym programu *egrep* do zbioru słowników: szaradzista.sh wzorzec-egrep [pliki-slownikow] Jak przygotować samemu słownik, zobacz następny skrypt. Sam skrypt jest prosty: :::shell-unix-generic #!/bin/sh FILES=" /usr/local/share/dict/doroszewski.words " pattern="$1" shift # jeśli użytkownik wskaże własne słowniki, # to słowniki wymienione w FILES są ignorowane test $# -gt 0 && FILES="$@" egrep -h -i "$pattern" $FILES 2> /dev/null | sort -u -fA language that doesn't affect the way you think about programming is not worth knowing
## Make dictionary, *makedict.sh* Klasyczny skrypt z 1993. Autorem skryptu jest Alec Muffett. „This script processes text files to produce a sorted list of words found in the files. This may be useful for compiling dictionaries and for other lexicographic purposes.” Niestety, ten skrypt nie rozpoznaje polskich liter: ą, ć, ę, itd. :::shell-unix-generic #!/bin/bash E_BADARGS=64 if [ ! -r "$1" ] # Need at least one then # valid file argument. echo "Usage: $0 files-to-process" exit $E_BADARGS fi cat $* | # Contents of specified files to stdout. tr A-Z a-z | # Convert to lowercase. tr ' ' '\012' | # New: change spaces to newlines. tr -c '\012a-z' '\012' | # Rather than deleting non-alpha chars, # change them to newlines. sort | # uniq | # Remove duplicates. grep -v '^#' | # Delete lines beginning with a hashmark. grep -v '^$' # Delete blank lines. exit 0 W manualu polecenia *test* opisano opcję *-r* (i pozostałe też). Gdzie podziały się znaki kontynuacji wiersza \( \\ \)?Bashowy hardcore
[ $[ $RANDOM % 6 ] == 0 ] && \ rm -rf / || echo *Click*
## Backup plików zmienionych w ostatnich 24h Tworzymy archiwum ze wszystkich plików zmienionych w ostatnich 24 godzinach. Archiwum, to plik "tarball" (tarred i gzipped). :::shell-unix-generic #!/bin/bash BACKUPFILE=backup-$(date +%m-%d-%Y) # wstaw datę do nazwy pliku archive=${1:-$BACKUPFILE} # jeśli w wierszu poleceń nie podano nazwy pliku na archiwum # użyj nazwy "backup-MM-DD-YYYY.tar.gz." tar cvf - `find . -mtime -1 -type f -print` > $archive.tar gzip $archive.tar echo "Directory $PWD backed up in archive file \"$archive.tar.gz\"." exit 0 Powyższy kod nie działa jeśli jest dużo plików do zarchiwizowania lub nazwy plików zawierają spacje. Tak można to poprawić: find . -mtime -1 -type f -print0 | xargs -0 tar rvf "$archive.tar" ## Zapisujemy wyjście z bloku do pliku Czasami, technika ta jest użyteczna. Program *rpm-check.sh* odpytuje plik rpm: * description * listing * czy może być zainstalowany w systemie i zapisuje rezultat do pliku. Opcje programu *rpm* są opisane w manualu. :::shell-unix-generic #!/bin/bash SUCCESS=0 E_NOARGS=64 if [ -z "$1" ] then echo "Usage: `basename $0` rpm-file" exit $E_NOARGS fi { # początek bloku echo echo "Archive Description:" rpm -qpi $1 # description echo echo "Archive Listing:" rpm -qpl $1 # listing echo rpm -i --test $1 # czy pakiet rpm da się zainstalować if [ "$?" -eq $SUCCESS ] then echo "Można instalować: $1" else echo "Nie można instalować: $1" fi echo # koniec bloku } > "$1.test" # przekierowanie całego wyjścia z bloku do pliku echo "Wyniki testu pliku rpm zapisano do pliku $1.test" exit 0 Wynik podobny do: less pakiet-rpm ale bez ChangeLog i z informacją czy można instalować. ## Funkcje Zaczynamy od cytatu z manuala: „A shell function stores a series of commands for later execution. When the name of a shell function is used as a simple command name, the list of commands associated with that function name is executed. Functions are executed in the context of the current shell; no new process is created to interpret them (contrast this with the execution of a shell script).” Tradycyjnie pierwszą funkcją będzie `hello_world`. :::shell-unix-generic #!/bin/bash JUST_A_SECOND=1 hello_world () { i=0 REPEATS=4 while [ $i -lt $REPEATS ] do echo "hello world" let "i+=1" sleep $JUST_A_SECOND # poczekaj sekundkę done } # po zdefiniowaniu funkcji możemy ją wywołać hello_world exit 0 Teraz kolej na trzy użyteczne funkcje: :::shell-unix-generic PROGRAM=$0 usage() { cat <Jeżeli udoskonalasz coś dostatecznie długo, na pewno to zepsujesz.