Praca interaktywna :: standardowe wejście wyjście i przekierowania :: łączenie poleceń :: sterowanie procesami :: działanie w tle :: sztuczki z *[]$()? :: Programowanie w Bash'u :: zmienne :: pętle i instrukcje wyboru :: funkcje :: skrypty z opcjami :: Linki i moje projekty

Bash

Bash jest jednym z najpopularniejszych interpretatorów poleceń (jest on oparty na składni sh), podobnie jak inne umożliwia on zarówno pracę interaktywną (wydawanie kolejnych poleceń przez operatora systemu), jak i pracę w trybie wsadowym (czyli wykonywanie skryptów powłokowych, stanowiących zapisane w pliku tekstowym ciągi poleceń).

Praca interaktywna

Praca interaktywna polega na wprowadzaniu ciągu poleceń (przy pomocy jakiejś formy klawiatury), które są wykonywane i ich wyniki widoczne są w postaci zmian w plikach/systemie plików lub w postaci komunikatów wyświetlanych na ekranie. Normalnie odpalony bash uruchamia się w trybie pracy interaktywnej, uprzednio czytając swoje pliki konfiguracyjne (m.in. ~/.bashrc). W trybie tym możemy:

  • korzystać z historii linii poleceń - przewijanie przy pomocy strzałek w górę i dół, wyszukiwanie przy pomocy Ctrl+R, z kolei umieszczając w pliku konfiguracyjnym export HISTIGNORE="&:ls:[bf]g:cd:exit:kill *" możemy ustawić ignorowanie powtórzeń (&) oraz wybranych komend (w podanym przykładzie kill z dowolnymi argumentami, ale nie killall);
  • korzystać z autouzupełniania wpisywanych nazw poleceń / ścieżek przy pomocy Tab, aby rozbudować tą funkcjonalność warto zainteresować się "bash completion" i dodać do swojego pliku konfiguracyjnego . /etc/bash_completion;
  • korzystać z zdeklarowanych w pliku konfiguracyjnym aliasów dla często wydawanych poleceń z zestawem opcji np. alias 'wlacz_komputer'='/usr/sbin/etherwake MAC:ADRES:KOMPUTERA:KTORY:LUBIMY:WLACZAC' lub function kman { command konqueror man:/"$@"; } (command zapewnia że wywołamy komendę a nie funkcję basha lub alias);
  • edytować linię polecenia (poruszanie się po niej strzałkami przód tył i home/end, kasowanie przy pomocy backspace/delete oraz wprowadzanie zmian).;
  • wykorzystywać wszystkie polecenia używane zazwyczaj w trybie wsadowym.

standardowe wejście wyjście i przekierowania

Do przekierowywania wejść i wyjść poleceń służą znaki:
| - przekierowanie wyjścia (polecenia z lewej strony) do programu (polecenia po prawej stronie)
> - przekierowanie wyjścia do pliku
< - pobieranie wejścia z pliku.
możliwe jest też przekierowanie stdout dwóch poleceń jako wejść plikowych - np. diff <(cat plik1) <(cat plik2). Dzięki konstrukcji exec 1> plik.txt można przekierować całe standardowe wyjście do pliku, natomiast zmienna=`cat` umożliwi wczystanie standardowego wejścia do pliku. Warto w tym miejscu wspomnieć także o programie tee, który wypisuje swoje standardowe wejście do pliku i na standardowe wyjście jednocześnie.

łączenie poleceń

Do łączenia poleceń oprócz przekierowywania wejść i wyjść służą także inne znaki:

  • rozdzielane średnikiem (lub niecytowanym znakiem nowej linii)- wykonywane wszystkie po kolei
  • rozdzielane || (LUB logiczne) wykonywane są aż któreś zwróci 1
  • rozdzielane && (I logiczne) wykonywane są od lewej aż któreś zwróci 0
  • rozdzielane & - polecenie poprzedzające przechodzi w tło i zwraca zero polecenie następujące wykonywane jest normalnie

Polecenia możemy także grupować przy pomocy nawiasów klamrowych { polecenia; } (uwaga średnik i spacje, które możemy ewentualnie zastapić znakami nowej linii są konieczne) lub nawiasów okrągłych (polecenia), wtedy wykonywane są w podpowłoce (uruchomiony jest osobny proces basha).

sterowanie procesami

W systemach unixowatych z uruchomionymi procesami możemy komunikować się m.in. przy pomocy sygnałów. Do ich wysyłania służy program kill. Warto jednak wspomnieć także o kombinacji klawiszy [Ctrl]+[c] wysyłającej sygnał SIGINT (nr. 2, przerwanie) oraz [Ctrl]+[/] wysyłającej sygnał SIGQUIT (nr. 3, zakończenie programu). Jeżeli jest już mowa o kombinacjach specjalnych kończących działanie programu to trzeba wspomnieć też o [Ctrl]+[d] wysyłającej znak ASCII o numerze 4 czyli EOT (koniec transferu) czyli informację o końcu wprowadzania danych tekstowych (nie jest to EOF ani ETX mające podobne znaczenie). Kombinacja ta wprowadzana np. w bashu kończy jego działanie.

działanie w tle

Celem uruchomienia polecenia w tle podajemy na końcu linii poleceń znak &. Listę tak uruchomionych zadań z aktualnego shela możemy wyświetlić poprzez polecenie jobs a następnie przenieść na pierwszy plan poleceniem fg %n lub zakończyć poprzez kill %n (gdzie n jest wyświetlonym przez jobs numerem zadania). Warto też wspomnieć o kombinacji [Ctrl]+[z] (wysyłającej sygnał SIGTSTP) zawieszającej wykonywanie aktualnego programu i powodującej ponowne przejście do shela, następnie można spowodować odwieszenie i przesunięcie w tło tak przerwanego procesu poprzez bg [%n].

sztuczki z *[]$()?

Warto też wspomnieć o możliwości korzystania ze znaków wieloznacznych: * - dowolny ciąg znaków niezaczynający się od kropki, ? - dowolny jeden znak, [-xawA-F] - dowolny ze znaków wymienionych : -, x, a, w oraz przedział od A do F. Aby podejrzeć jakie pliki zostaną dopasowane do naszego wzorca polecam echo - np. echo ?[a-z]* wypisze wszystkie pliki mające na drugiej pozycji mała literę (w Unixach wielkość liter ma znaczenie).

Jest także możliwość przekazania wyniku polecenia jako parametru innego polecenia: inne_polecenie `polecenie`, jeżeli chcemy jeszcze bardziej zagnieżdzać zamiast ` w Bashu możemy używać konstrukcji z $(), np. inne_polecenie $(polecenie). Natomiast przy pomocy nawiasów kwadratowych możemy wykonywać podstawowe operacje artmetyczne - np. echo $[2+2] wypisze 4; podobnie działają podwójne nawiqsy okrągłe - $(( wyrazenie )), np. echo $((2+2)) też wypisze 4; natomiast do bardziej zaawansowanej matematyki wykorzystać można program bc - echo "2+2" | bc robi to samo ... (więcej man 1 bc).

Kolejną sztuczką jest wbudowane przetwarzanie napisów przy pomocy {%}, np. echo ${PATH%:*} wybisze zawartość zmiennej $PATH bez ostatniego członu (po ostatnim :), proktycznym zastosowaniem może być np. masowa zamiana rozszerzeń plików for f in *.txt ; do mv "$f" "${f%.txt}.htm"; done. Inne zastosowanie klamerek (podstawienia wielkrotne każdy z każdym) pokazuje echo A{7,8}X{1,2}. Warto także wspomnieć o różnicy między echo $zmienna a echo "$zmienna", gdy zmienna zawiera znaki nowej linii (w pierwszym wypadku zostaną ope zastąpione spacjami). Więcej o tego typu sztuczkach w części o zmiennych.

Programowanie w Bash'u

Praca w trybie wsadowym polega na wykonywaniu zapisanego w pliku tekstowym ciągu poleceń. Istnieje kilka metod wykonania takiego pliku:

  • . sciezka_do_pliku lub >source sciezka_do_pliku - polecenia zwarte w podanym pliku wykonane zostaną przez bieżącą instancję basha (przydatne do włączania plików z zmiennymi konfiguracyjnymi, aby wczytać tylko zmienne z pliku (bez wykonywania zawartych w nim poleceń) możemy skozystać z następującego kodu eval export `cat config.sh`, wczyta on nawet prawie poprawnie wieloliniowe zmienne z podanego pliku - nowa linia zostanie zastąpiona spacją)
  • bash sciezka_do_pliku - zostanie uruchomiony osobny proces powłoki który wykona polecenia zwarte w podanym pliku
  • sciezka_do_pliku (w przypadku gdy jest w bieżącym katalogu to rozpoczęta od ./) - jak wyżej z tym że plik musi mieć prawa wykonywalności i zazwyczaj rozpoczyna się (pierwsza linijka) od określenia interpretatora w przypadku Bash'a będzie to #!/bin/bash (uwaga na niektórych systemach bash może być też albo tylko (jak w BeOSie) określany przez ścieżkę /bin/sh); jeżeli taki plik zostanie umieszczony w katalogu będącym w ścieżce wyszukiwania (zmienna $PATH) to może być odpalany po nazwie

Skrypty często mają rozszerzenia .sh, jednak także często odchodzi się od tego aby skrypt wywoływany był w oparciu o samą nazwę (tak jak dowolna inna komenda).

Pisanie skryptów bashowych jest w zasadzie typowym programowaniem (i wbrew pozom ten język programowania ma ogromne możliwości i wiele zagadnień można rozwiązać w nim bardzo łatwo i szybko). Program taki korzysta z dowolnych instrukcji powłoki (cd, ...) oraz wywołań innych programów (cp, rm, gawk, grep, ...). W śród wywoływanych zewnętrznych programów na szczególną miejsce zasłużył sobie (g)awk, będący interpretowanym językiem programowania służącym do przetwarzania tekstów. Z kolei wśród poleceń wbudowanych powłoki jest bardzo wiele instrukcji przeznaczonych głównie dla skryptów (aczkolwiek każdą z nich można wykorzystać w trybie interaktywnym). W tym miejscu warto także wspomnieć o pakietach takich jak zenity, kdialog, gdialog umożliwiających wyświetlenie z poziomu skryptu okienek dialogowych środowiska graficznego (X'ów) a także o dcop umożliwiającym sterowanie programami KDE z powłoki. Przydatne może być też określenie poleceń wykonywanych gdy następuje przerwanie wykonywania skryptu (np. Ctrl+C lub kill -15) - trap "{ echo "to koniec"; }" EXIT.

zmienne

Jak w każdym przyzwoitym języku programowania możemy korzystać ze zmiennych.

Na wstępie warto wspomnieć o paru parametrach (jest ich trochę więcej) do których dostęp mamy prze $:

  • 0 - nazwa skryptu lub powłoki
  • liczba - odpowiedni argument wywołania (parametr pozycyjny)
  • * - kolejne parametry pozycyjne, równoważne "$1 $2 ..."
  • @ - kolejne parametry pozycyjne, równoważne "$1" "$2" ...
  • # - liczba parametrów pozycyjnych
  • ? - kod zakończenia ostatniej instrukcji

Prawie wszystkie zmienne w bashu mają zasięg globalny. Wyjątkiem są zmienne odpowiedzialne za przechowywanie argumentów skryptu / funkcji (każda funkcja ma swój niezależny zestaw i nie ma dostępu do argumentów skryptu) oraz zmienne definiowane przy pomocy słowa kluczowego local przed nazwą zmiennej. Kolejnym wyjątkiem są zmienne przekazywane do podprocesów - są one w nich dostępne, ale ich zmiany nie są widoczne w głównym skrypcie - np. (odkomentowanie linii z ps pokazuje dlaczego tak się dzieje):

zm=1
#ps x -l | tail
echo -e "ttt\nooo" | while read f; do
	let zm++
	echo " $zm"
	#ps x -l | tail
done
echo $zm

Dostępne są też zmienne środowiskowe - takie jak $HOME czy też $PATH, a także np. pseudozmienna $RANDOM zwracająca losową liczbę. Przydatna jest także komenda eval wykonująca komendę powstałą z zczytania przekazanych do niej argumentów (w tym zmiennych) - dzięki jej użyciu możemy np. część konstrukcji case, if czy jakiejś pętli przechowywać w zmiennej (np. LISTA_WYBORU="a) echo AA;; b) echo BB;;"; eval case b in $LISTA_WYBORU esac).

Możliwe jest także (na co najmniej 3 sposoby) wydobycie zawartości zmiennej, której nazwę mamy w innej zmiennej:

A="to chcemy wyświetlić"; B=A;

# metoda pierwsza
C=${!B}; echo $C

# metoda druga
C='eval "echo \$$B"'; D=`eval "$C"`; echo $D

# metoda trzecia
C=$(C='eval "echo \$$B"'; eval $C); echo $C
#!/bin/bash

# to jest komentarz

ala="ma kota"
wiek_kota=5
# powyzej zdefiniowalismy dwie zmienne, wazne aby miedzy nazwa a = nie bylo spacji

echo "Ala $ala, który ma $wiek_kota lat"
# oraz podstawiliśmy je - służy do tego operator $

# tutaj też uwaga odnośnie różnych cudzysłowów:
# "" - zmienne są podstawiane itp, '' - tekst nie jest modyfikowany,
# `` - służy do wykonania polecenia, którego stdout może być zapisany w zmiennej
#      w Bashu takie samo działanie ma $() ta metoda może być zagnierzdzana
# (jak w poniższym przykładzie):
wynik=`ls $HOME`
# tutaj widzimy też że w ten sam sposób możemy obsługiwać zmienne środowiskowe ...
# warto także zaznaczyć że aby zmienić zmienne środowiskowe tak aby zmiany były
# widoczne poza skryptem po  nadaniu nowej wartości zmiennej nalezy skorzystać z
# komendy: export $ZMIENNA

echo "${ala}makota"
# klamerkami zaznaczyliśmy co jest nazwa zmiennej a co napisem ...


echo ${nie_ma_takiego:-"Hello World"}
# wypisze $nie_ma_takiego gdy ustawiony i niepusty albo (w przeciwnym razie) "Hello World"
# gdy zamiast :- użyjemy := dodatkowo zmienna nie_ma_takiego zostanie ustawiona na "Hello World"

# jest jeszcze pare ciekawych zastosowań ${} ...

echo ${ala:+"Hello World"}
# podobnie jak powyżej, ale wypisze "Hello World" gdy $ala jest zdefiniowana (nie pusta)
# w przeciwnym razie użyje napisu pustego,

# gdy w powyższych pominiemy : zdeklarowany napis pusty będzie rozróżniany od wartości niezdeklarowanej


echo ${#ala}
# wypisze długość napisu w zmiennej ala

abc="abcdefgh"
echo ${abc#"ab"} ${abc#"h"} ${abc:1} ${abc:2:2}
# wypisze $abc:
#	z odrzuconym początkowym ab
#	końcowym h
#	od pozycji 1 (pierwsza litera to pozycja 0)
#	2 litery od pozycji 2

${abc:0:$[ ${#abc} - 1 ]}
# powyższa dziwna konstrukcja służy wypisaniu zawartości zmiennej bez ostatniego znaku

abc="abcdeabcf"
echo ${abc/ab/AB} ${abc//ab/AB}
# wypisze $abc z zastąpionym pierwszym (a potem wszystkimi) wystąpieniami ab przez AB
# jeżeli napis dopasowywany rozpoczniemy od # musi on być na początku zmiennej
# jeżeli od % musi być na końcu

# podobne rzeczy można robić korzystając z expr - w szczególności
#	expr match $zmienna 'wyrazenieregularne1\(podwyrazenie\)wyrazenieregularne2'
# zwróci część podanej zmiennej pasującej do podanego podwyrażenia ujętego w \( \)
expr $abc : '.*\([^df]*\)'


# możemy używać $ wewnątrz napisów - w tym celu zabezpieczmy go \ lub napis umieszczmyu w ''
xx='${ala} ma $HOME'
ala="ALA"
export ala
echo $xx
# jak widać zmienne wtedy nie są podmieniane, ale jeżeli wyeksportujemy zmienną
# i przekażemy taki napis do envsubst to zostaną podstawione
echo $xx | envsubst
# możemy nawet określić które mają być podstawiane ...
echo $xx | envsubst '$ala'


# ale to jeszcze nie wszystko bash umożliwia nam kozystanie z TABLIC:
tablica[0]="to jest element 0"
tablica[1]="to jest element 1"
element=1
echo ${tablica[1]} " --- " ${tablica[0]}

# @ i * zachowują się tak jak w $@ i w $* ...
echo ${tablica[@]}

echo ${#tablica[@]}
# zwróci ilość elementów w tablicy, ale
echo ${#tablica[1]}
# zwróci długość elementu o indeksie 1

# pozostałe operacje napisowe gdy są stosowane do ${tablica[@]} stosują się do
# całej zawartości tablicy a gdy ${tablica[$i]} do elementu określonego w $i ...

XHTML generated by highlight 2.4.5 (http://www.andre-simon.de/) from zmienne.sh

pętle i instrukcje wyboru

Do dyspozycji mamy także pętle oraz instrukcje wyboru. W śród pętli warto zwrócić uwagę iż mogą one funkcjonować w oparciu o warunek zawarty w nawiasach kwadratowych lub też na liście plików / słów, czy też do czasu aż jakaś komenda się wykonuje.

#!/bin/bash

for nazwa in /tmp/* ; do
        echo $nazwa;
done
# powyzsza petla wypisze nazwy wszystkich plików znajdujących się w /tmp

for (( i=0 ; $i<=20 ; i++ )) ; do
        echo $i;
done
# powyzsza petla wypisze liczby od 0 do 20

cat /etc/fstab | while read slowo reszta_linii; do
        echo $reszta_linii;
done
# powyższa pętla wypisze po kolei wszystkie wiersze pliku przekazanego przez stdin
# (cat nazwa_pliku |) z pominięciem pierwszego słowa (pierwsze słowo wczytywane było
# do zmiennej slowo, której nie wypisywaliśmy) jest też pętla until która odpowiada
# while z zanegowanym warunkiem (w tym wypadku warunkiem jest instrukcja read)

XHTML generated by highlight 2.4.5 (http://www.andre-simon.de/) from for.sh

Dość praktycznym zastosowaniem pętli w bashu jest wywoływanie czegoś dla każdej linii wejścia - np. grep -r 'warunek' * | cut -f1 -d':' | uniq | while read f ; do echo $f; sed 's#szukany#zamiennik#g' "$f" > /tmp/zam; mv -f /tmp/zam "$f"; done . Kod ten wyszukuje rekurencyjnie pliki zawierające "warunek" oraz zamienia w nich wszystkie wystąpienia "szukany" na "zammiennik". Warto tutaj zwrócić uwagę na wykorzystanie pętli while oraz jednolinijkowy zapis (wygodny przy pracy interaktywnej z bashem).

Więcej informacji o warunkach jakie możemy zawrzeć w [] wykorzystywanym zarówno w pętlach jak i warunkach typu if patrz: man 1 test.

#!/bin/bash

# ponizszy wpis wlacza wypisywanie wykonywanych polecen
#- bardzo przydatne przy debugowaniu skryptow ...
set -x

COS="ryba";

# poniższy fragment obrazuje instrukcje case na prostym przykładzie
case $COS in
        kot | pies)
                echo  "kot lub pies"
                ;;
        ryba)
                echo  "ryba"
                ;;
        *)
                echo  "cos innego"
                ;;
esac

# następny fragment działa dokładnie tak samo tyle że na instrukcji if
if [ "$COS" = "kot" -o "$COS" = "pies" ]; then
        echo  "kot lub pies";
elif [ "$COS" = "ryba" ];  then
        echo  "ryba"
else
        echo  "cos innego"
fi

XHTML generated by highlight 2.4.5 (http://www.andre-simon.de/) from case_if.sh

funkcje

Bash umożliwia również definiowanie funkcji, z których możemy potem korzystać jak z zwykłych poleceń:

#!/bin/bash

# deklaracja funkcji o nazwie wypisz, argumenty podstawiane są jako $1 $2 ...
function wypisz() {
        echo "$1 World !!!"
}

# wywolanie funkcji petla z jednym argumentem - Witaj
wypisz Witaj

XHTML generated by highlight 2.4.5 (http://www.andre-simon.de/) from function.sh

skrypty z opcjami

Przy zastosowaniu narzędzi takich jak getopt, getopts możliwe jest pisanie skryptów dobrze reagujących na przekazywane opcje i parametry (w taki sposób jak normalne unix'owe programy). Poniżej prezentuję rozwiązanie oparte na programie "getopt", pozwala ono na korzystanie zarówno z długich jak i krótkich opcji (łatwiejsza w zastosowaniu komenda getopts ma niestety problem z opcjami długimi). Alternatywne rozwiązanie oparte na awk zawarte jest w engine.sh.

#!/bin/bash

# przetworzenie argumentów określonych w wywołaniu getopt
# dalsze argumenty dostępne są w $1, $2, itd po zakończeniu pętli
# : -> poprzedzająca opcja wymaga argumentu
# :: -> poprzedzająca opcja może mieć argument
# uwaga o ile wymagane argumenty moga byc rozdzielane od opcji spacją o tyle opcjonalne nie
# (krótkie należy podawać zaraz po opcji, długie rozdzielając =)
eval set -- "`getopt -o xy:z:: -l opcja-x,opcja-y:,opcja-z:: -- "$@"`"
while true; do
	case $1 in
		-x|--opcja-x) echo "X";;
		-y|--opcja-y) echo "Y, argument \`$2'"; shift;;
		-z|--opcja-z) echo "Z, argument \`$2'"; shift;;
			# tuataj $2 może być pusty (gdy nie podano), ale jest zdefiniowany
		--) shift; break;;
	esac
	shift;
done

XHTML generated by highlight 2.4.5 (http://www.andre-simon.de/) from opcje.sh

Linki i moje projekty

Zachęcam również do przyjrzenia się kilku prostym skryptom bashowym:

  • arp_log.sh - bardzo prosty skrypcik logujący w zadanych odstępach czasu aktualny wygląd tablicy ARP do pliku
  • fs_backup.sh - kolejny bardzo prosty skrypcik wykonujący backup wskazanej partycji na innej z wykorzystaniem rsync
  • progress_bar.sh - prosty skrypcik wypisujący kropki w trakcie działania innego procesu
  • check_sum.sh - wykonuje rekurencyjnie na plikach i podkatalogach zadanego katalogu md5sum
  • kamera.sh - robi zdjęcie aparatem cyfrowym aż się uda
  • radio.sh - z wykorzystaniem DCOP uruchamia zestaw aplikacji do słuchania radia
  • commons-config.sh (i commons-config.conf) - Skrypt przeznaczony do obsługi współdzielonego (sieciowo) konfiguracji pomiędzy kilkoma hostami. Korzysta do tego celu z sshfs oraz svn i rsync, do startu i stopu korzysta z skryptów dodawanych do .kde/env i .kde/shutdown wyświetlających swój progressbar. Skrypt ponadto eksportuje klucze i certyfikaty szyfrujące oraz inne ustawienia przechowywane w $HOME oraz wykonuje "zaawansowane" tarowanie celów linków.
  • magnetowid.sh - nagrywanie obrazu z karty TV z wykorzystaniem transcode
  • vlc_ser_RTV.sh - transmisja sieciowa sygnału RTV z wykożystaniem VLC
  • beos_mail2mbox.sh - prosty skrypt bashowo-awkowy do konwersji skrzynki pocztowej z BeOSa do standardowego mbox'u
  • net_stat.sh - prosty skrypt bashowo-awkowy do generowania statystyk transferu na wybranym interfejsie sieciowym
  • mymenu_install.sh - skrypt instalujący menu w KDE (z ciekawszych rozwiązań warto wspomnieć o ustalaniu ścieżki katalogu w którym znajduje się plik skryptu, sprawdzaniu czy dwa katalogi nie są tym samym katalogiem, podmianie pierwszego wystąpienia napisu w pliku)
  • engine.sh - dość rozbudowany skrypt bash'owo-awk'owy obsługujący moją stronę www
  • archiwum z skryptami do przygotowania pozycji dodawanych do systemu prezentacji starodruków opracowanymi na potrzeby projektu DIROP prowadzonego w ICM UW (wersja rozpakowana)

Polecam także zajrzeć do artykułu poświęconego językowi AWK, który jest bardzo użyteczny w połączeniu z bash'em.

Więcej informacji o Bash'u: man 1 bash. Zobacz w Sieci: Kurs BASHa, Advanced Bash-Scripting HOWTO, Kurs basha, Kolorowe Powłoki, Przydatne proste skrypty dla linuxa, Bash w przykładach (z Dokumentacji Gentoo Linux): część 1, część 2, część 3, Powłoka - ogólnie o niej.


Copyright (c) 1999-2008, Robert Paciorek (http://www.opcode.eu.org/), BSD-type license


Redystrybucja wersji źródłowych i wynikowych, po lub bez dokonywania modyfikacji JEST DOZWOLONA, pod warunkiem zachowania niniejszej informacji o prawach autorskich. Autor NIE ponosi JAKIEJKOLWIEK odpowiedzialności za skutki użytkowania tego dokumentu/programu oraz za wykorzystanie zawartych tu informacji.

This program is free software. Redistribution and use in source and binary forms, with or without modification, ARE PERMITTED provided save this copyright notice. This document/program is distributed WITHOUT any warranty, use at YOUR own risk.

Valid XHTML 1.1 Dokument ten (URL: http://www.opcode.eu.org/bash) należy do serwisu OpCode. Autorem tej strony jest Robert Paciorek, wszelkie uwagi proszę kierować na adres e-mail serwisu: webmaster@opcode.eu.org.
Data ostatniej modyfikacji artykulu: 2008-08-24 17:58:33 (UTC) (data ta może być zafałszowana niemerytorycznymi modyfikacjami artykułu).