[#] Tworzymy skrypty .sh - podręcznik

( Ostatnio zmieniony wt., 17/06/2008 - 12:49 )
 

Skrypt zaczynamy od nagłówka

#!/bin/sh
....

Oczywiście to zależy w jakiej powłoce chcemy aby nasz skrypt był uruchamiany. Domyślnie SH.

Nadajemy skryptowi prawo do wykonania:

chmod u+x nasz_skrypt.sh

Komentarze

Komentarze zaczynają się od symbolu #. Wszystkie pozostałe znaki aż do końca linii są ignorowane.

Argumenty

Skrypty – podobnie jak inne programy – możemy uruchamiać podając dodatkowe argumenty. Poniższe zmienne o specjalnych nazwach pozwalają odczytywać te argumenty:

$#
liczba argumentów,
$$
PID procesu bieżącej powłoki
$?
kod powrotu ostatnio wykonywanego polecenia
$0
nazwa naszego skryptu
$1, $2, …
odpowiednio pierwszy argument, drugi argument, itd.,
$@
lista wszystkich argumentów; przydatne jeśli chcemy przekazać wszystkie argumenty innemu programowi. Jeśli chcemy mieć pewność, że każdy argument będzie osobnym słowem, należy użyć cudzysłowów: „$@“; ma to znaczenie na przykład wtedy, gdy istnieje argument, który zawiera spację.

Aby operować na dalszych argumentach pomocne jest polecenie shift , które usuwa pierwszy argument, a pozostałe przesuwa o jeden w lewo. Aby n-krotnie wywołać polecenie shift wystarczy podać mu to n jako argument: shift n .

Test argumentów

Stwórzmy skrypt o nazwie nasz_skrypt.sh a w nim wklejmy poniższy przykład:

#!/bin/sh
  # Testowanie argumentów
  echo "Uruchomiłeś program `basename $0`"
  echo Wszystkie: $@
  echo "Pierwsze trzy: '$1', '$2', '$3'"
  shift 2
  echo "shift 2"
  echo "Wszystkie: $@"
  echo "Pierwsze trzy: '$1', '$2', '$3'"

Wywołanie naszego skryptu następuje po wykonaniu: ./nasz_skrypt.sh jeden dwa trzy cztery

Wynik działania:

Uruchomiłeś program nasz_skrypt.sh
Wszystkie: jeden dwa trzy cztery
Pierwsze trzy: 'jeden', 'dwa', 'trzy'
shift 2
Wszystkie: trzy cztery
Pierwsze trzy: 'trzy', 'cztery', ''

$(( … )) i (( … ))

Forma $(( wyrażenie )), w stosunku do expr... jest o wiele bardziej wygodna. Jest tylko jeden problem, ta składnia może nie działać w innych shellach, czy w starszych wersjach Basha. Pierwszą zaletą jest szybkość, tzn. użycie tej składni nie powoduje tworzenia nowego procesu (co ma miejsce w przypadku expr ...) i jest interpretowane bezpośrednio przez Basha. Po drugie przy odwoływaniu się do zmiennych nie musimy poprzedzać ich znakiem $, gdyż każdy identyfikator wewnątrz podwójnych nawiasów jest traktowany jak zmienna. Nie musimy także dbać o używanie odstępów i backslashowania znaków specjalnych. Trzecią zaletą jest bogatsza paleta operatorów arytmetycznych. Otóż wyrażenia arytmetyczne mogą tu zawierać dowolne operatory, które można znaleźć w języku C, np. inkrementacje/de­krementacje zmiennych (ID++, –ID), operacje bitowe (<<, &, ~), przypisania arytmetyczne (=, +=, *=), itp. Więcej o znaczeniu tych operacji i dozwolonych działaniach można znaleźć w kursie języka C lub w dokumentacji Basha.

Składni (( wyrażenie )) używamy wtedy, gdy nie potrzebujemy wyniku, czyli wtedy, gdy wyrażenie nie jest częścią instrukcji, tylko jest sama w sobie instrukcją.

Przykłady:

Kilka sposobów na zwiększenie zmiennej o 1:

bashtest@host:~$ a=0
bashtest@host:~$ a=$((a + 1))
bashtest@host:~$ ((a=a+1))
bashtest@host:~$ ((a++))
bashtest@host:~$ ((a += 1))
bashtest@host:~$ echo $a
4
bashtest@host:~$

lub:

#!/bin/bash

  echo $((8/2))

  wynik=$[4*5/2]
  echo "$wynik"

lub

#!/bin/bash

  for pliki_html in $(ls *.html)
  do
    numer=$((numer+1))
    echo "$numer. "
    echo $pliki_html
  done

Wynikiem będzie ponumerowana lista wszystkich plików o rozszerzeniu .html, znajdujących się w bieżącym katalogu.

lub

bashtest@host:~$ echo "1 + ... + $x = $((x * (x + 1) >> 1))"
1 + ... + 5 = 15
bashtest@host:~$ echo $((x++))
5
bashtest@host:~$ echo $((++x))
7
bashtest@host:~$ echo $((x += x > 0))
8
bashtest@host:~$ echo "x = $x"
x = 8
bashtest@host:~$

lub

#!/bin/bash

  GDZIE_JESTEM=$(pwd)
  echo "Jestem w katalogu $GDZIE_JESTEM"

let

jest wbudowanym poleceniem Basha i używamy go, podając mu jako argumenty wyrażenia do przetworzenia. Używając let trzeba pamiętać, że wyrażenie zawierające odstępy trzeba ujmować w cudzysłowy, aby formowały jeden argument.

let wyrażenie1 wyrażenie2 ...

Przykład:

bashtest@host:~$ x=0
bashtest@host:~$ let x+=2 "x += 4"
bashtest@host:~$ echo $x
6
bashtest@host:~$

Odczytywanie danych z klawiatury – polecenie READ

#!/bin/bash
  echo "Wpisz cztery wartości:"

  read a b c
  echo "Wartość zmiennej a to: $a"
  echo "Wartość zmiennej b to: $b"
  echo "Wartość zmiennej c to: $c"

Nie przypadkiem w powyższym przykładzie pojawiło się polecenie wpisania czterech wartości, pierwsza wartość trafi do zmiennej a, druga do zmiennej b, natomiast trzecia i czwarta oraz rozdzielające je znaki separacji przypisane zostaną zmiennej c.

Wybrane opcje polecenia READ

-p (pokaże znak zachęty bez kończącego znaku nowej linii)

#!/bin/bash

  read -p "Pisz:" odp
  echo "$odp"

-a (kolejne wartości przypisywane są do kolejnych indeksów zmiennej tablicowej)

#!/bin/bash
  echo "Podaj elementy zmiennej tablicowej:"

  read tablica
  echo "${tablica[*]}"

-e (jeśli nie podano żadnej nazwy zmienej, wiersz trafia do $REPLY)

#!/bin/bash
  echo "Wpisz coś:"

  read -e
  echo "$REPLY"

ECHO – wyświetlamy napisy

Parametry polecenia echo:

* -n nie jest wysyłany znak nowej linii
* -e włącza inetrpretacje znaków specjalnych takich jak:
       \a czyli alert, usłyszysz dzwonek
       \b backspace
       \c pomija znak kończący nowej linii
       \f escape
       \n form feed czyli wysuw strony
       \r znak nowej linii
       \t tabulacja pozioma
       \v tabulacja pionowa
       \\ backslash
       \nnn znak, którego kod ASCII ma wartość ósemkowo
       \xnnn znak, którego kod ASCII ma wartość szesnastkowo

Zapisanie tekstu do pliku:

echo "jakiś napis" > plik

Dodanie tekstu do pliku (na końcu):

echo "jakiś napis" >> plik

IF – warunki

Najprostszym rodzajem warunku jest: jeśli pierwszy program zwrócił 0 wykonaj program 2, lub odwrotnie: jeśli pierwszy program zwócił wartość różną od zera wykonaj program 2. Wykonanie czegoś takiego jest banalnie proste. W skrypcie, który zawiera taką linijkę:

program1&&program2

program2 będzie uruchomiony, jeśli program1 zwróci 0, natomiast w przypadku wpisania

program1||program2

program2 będzie uruchomiony, jeśli program1 zwrócił wartość różną od zera. Można łączyć te dwie formy, na przykład:

cat plik | grep -q aaa && echo "Znaleziono" || echo "Nie znaleziono"
if cd $katalog; then
  echo "Jesteśmy w katalogu $katalog"
else
  echo "Nie udało się wejść do katalogu $katalog"
fi

Pełna składnia if jest następująca:

if warunek1; then
  instrukcje1
elif warunek2; then
  instrukcje2;
...
else
  instrukcje_else;
fi

Przykłady:

#!/bin/bash
  if [ -x  /opt/kde/bin/startkde ]
  then
    echo "Masz KDE w katalogu /opt"
  elif [ -x /usr/bin/startkde ]
    echo "Masz KDE w katalogu /usr"
  elif [ -x /usr/local/bin/startkde ]
    echo "Masz KDE w katalogu /usr/local"
  else
    echo "Nie wiem gdzie masz KDE"
  fi

A to kilka przykładów operatorów polecenia test:

* -a plik istnieje
* -b plik istnieje i jest blokowym plikiem specjalnym
* - plik istnieje i jest plikiem znakowym
* -e plik istnieje
* -h plik istnieje i jest linkiem symbolicznym
* = sprawdza czy wyrażenia są równe
* != sprawdza czy wyrażenia są różne
* -n wyrażenie ma długość większą niż 0
* -d wyrażenie istnieje i jest katalogiem
* -z wyrażenie ma zerową długość
* -r można czytać plik
* -w można zapisywać do pliku
* -x można plik wykonać
* -f plik istnieje i jest plikiem zwykłym
* -p plik jest łączem nazwanym
* -N plik istnieje i był zmieniany od czasu jego ostatniego odczytu
* plik1 -nt plik2 plik1 jest nowszy od pliku2
* plik1 -ot plik2 plik1 jest starszy od pliku2
* -lt mniejsze niż
* -gt większe niż
* -ge większe lub równe
* -le mniejsze lub równe

Instrukcja CASE

#!/bin/bash
  echo "Podaj cyfrę dnia tygodnia"
  read d
  case "$d" in
    "1") echo "Poniedziałek" ;;
    "2") echo "Wtorek" ;;
    "3") echo "Środa" ;;
    "4") echo "Czwartek" ;;
    "5") echo "Piątek" ;;
    "6") echo "Sobota" ;;
    "7") echo "Niedziela" ;;
    *) echo "Nic nie wybrałeś"
  esac

Pętla FOR

for zmienna in lista
do
  polecenie
done

Przykład zmiany nazwy plików pisane DUŻYMI literami na nazwy pisane małymi literami::

#!/bin/bash
  for nazwa in *
  do
    mv $nazwa `echo $nazwa tr '[A-Z]' '[a-z]'`
  done

Pętla WHILE

while warunek
do
  polecenie
done

Przykłady:

#!/bin/bash
  x=1;
  while [ $x -le 10 ]; do
    echo "Napis pojawił się po raz: $x"
    x=$[x + 1]
  done

Pętla UNTIL

until warunek
do
  polecenie
done

Przykład:

#!/bin/bash
  x=1;
  until [ $x -ge 10 ]; do
    echo "Napis pojawił się po raz: $x"
    x=$[x + 1]
  done

Instrukcja SELECT

select zmienna in lista
do
  polecenie
done

Przykłady:

#!/bin/bash
  echo "Co wybierasz?"
  select y in X Y Z Quit
  do
    case $y in
      "X") echo "Wybrałeś X" ;;
      "Y") echo "Wybrałeś Y" ;;
      "Z") echo "Wybrałeś Z" ;;
      "Quit") exit ;;
      *) echo "Nic nie wybrałeś"
    esac
  break
  done

lub

#!/bin/bash
  echo ""
  echo "[ JAKI WINDOW MANAGER URUCHOMIĆ? WYBIERZ CYFRĘ Z LISTY: ]"
  echo ""
  select l in BLACKBOX ENLIGHTENMENT GNOME ICEWM KDE MWM OPENWIN TWM WMAKER WYJŚCIE
  do
    case "$l" in
      "BLACKBOX") cat /etc/X11/xinit/xinitrc.blackbox > ~/.xinitrc; startx $@ ;;
      "ENLIGHTENMENT") cat /etc/X11/xinit/xinitrc.e > ~/.xinitrc; startx $@ ;;
      "GNOME") cat /etc/X11/xinit/xinitrc.gnome > ~/.xinitrc; startx $@ ;;
      "ICEWM") cat /etc/X11/xinit/xinitrc.icewm > ~/.xinitrc; startx $@ ;;
      "KDE") cat /etc/X11/xinit/xinitrc.kde > ~/.xinitrc; startx $@ ;;
      "MWM") cat /etc/X11/xinit/xinitrc.mwm > ~/.xinitrc; startx $@ ;;
      "OPENWIN") cat /etc/X11/xinit/xinitrc.openwin > ~/.xinitrc; startx $@ ;;
      "TWM") cat /etc/X11/xinit/xinitrc.twm > ~/.xinitrc; startx $@ ;;
      "WMAKER") cat /etc/X11/xinit/xinitrc.wmaker > ~/.xinitrc; startx $@ ;;
      "WYJŚCIE") exit ;;
      *) startx $@
    esac
  break
  done

lub

#!/bin/bash
  echo ""
  echo "[ JAKI WINDOW MANAGER URUCHOMIĆ? WYBIERZ CYFRĘ Z LISTY: ]"
  echo ""
  select l in BLACKBOX ENLIGHTENMENT GNOME ICEWM KDE MWM OPENWIN TWM WMAKER WYJŚCIE
  do
    case "$l" in
      "BLACKBOX") echo "exec blackbox" > ~/.XClients; startx $@ ;;
      "ENLIGHTENMENT") echo "exec enligtenment" > ~/.XClients; startx $@ ;;
      "GNOME") echo "exec gnome-session" > ~/.XClients; startx $@ ;;
      "ICEWM") echo "exec icewm" > ~/.XClients; startx $@ ;;
      "KDE") echo "exec startkde" > ~/.XClients; startx $@ ;;
      "MWM") echo "exec mwm" > ~/.XClients; startx $@ ;;
      "OPENWIN") echo "exec openwin" > ~/.XClients; startx $@ ;;
      "TWM") echo "exec twm" > ~/.XClients; startx $@ ;;
      "WMAKER") echo "exec wmaker" > ~/.XClients; startx $@ ;;
      "WYJŚCIE") exit ;;
      *) startx $@
    esac
  break
  done

Zmienne

tablicowe

W BASH'u nie ma maksymalnego rozmiaru tablic. Kolejne wartości zmiennej tablicowej indexowane są przy pomocy liczb całkowitych, zaczynając od 0.

#!/bin/bash

  tablica=(element1 element2 element3)

  # dodajemy element4 do tablicy
  tablica[3]=element4

  echo ${tablica[0]}
  echo ${tablica[1]}
  echo ${tablica[2]}
  echo ${tablica[3]}

lub

Poniższy skrypt robi to samo co wcześniejszy.

#!/bin/bash

  tablica=(element1 element2 element3)

  echo ${tablica[*]}

Można też uzyskać długość (liczba znaków) danego elementu tablicy:

#!/bin/bash

  tablica=(element1 element2 element3)

  echo ${#tablica[0]}

Polecenie echo ${#tablica[0]} wydrukuje liczbę znaków z jakich składa się pierwszy element tablicy: element1 wynik to 8. W podobny sposób można otrzymać liczbę wszystkich elementów tablicy, wystarczy jako wskaźnik podać: @ lub *.

Usuwanie elementu tablicy:

unset tablica[2]

lub całej tablicy:

unset tablica[*]

lub

unset tablica[@]

globalne i lokalne

# globalne – widoczne w każdym podshellu # lokalne – widoczne tylko dla tego shella w którym został ustawione

Deklaracja zmiennej globalnej:

export x="napis"

lub jej anulowanie:

export -n x

Aby zobaczyć aktualnie ustawione zmienne globalne wykonaj:

export -p

lub

printenv

Cytowanie znaków specjalnych

Znaki cytowania służą do usuwania interpretacji znaków specjalnych przez powłokę.

* $ wskazuje na nazwę zmiennej, umożliwiając podstawienie jej wartości
* \ znak maskujący
* ` ` odwrotny apostrof, umozliwia zacytowanie polecenia

Przykłady:

#!/bin/bash

  x=2
  echo "Wartość zmiennej x to $x"    #wydrukuje Wartość zmiennej x to 2
  echo -ne "Usłyszysz dzwonek\a"
  echo "Polecenie date pokaże: `date`"
#!/bin/bash

  echo '$USER' #nie wypisze twojego loginu
#!/bin/bash

  x=`ls -la $PWD`
  echo $x  #pokaże rezultat polecenia

Alternatywny zapis, który ma takie samo działanie wygląda tak:

#!/bin/bash

  echo $(ls -la $PWD)

Jeśli chcesz by na ekranie pojawił się napis $HOME:

echo "$HOME"  #wydrukuje /home/ja

aby wyłączyć interpretacje przez powłokę tej zmiennej, trzeba napisać:

echo \$HOME    #i jest napis $HOME

Funkcje

#!/bin/bash

  function napis {
    echo "To jest napis"
  }

Nazwę funkcji umieszczamy po słowie kluczowym function, w powyższym przykładzie mamy funkcje o nazwie napis, odwołujemy się do niej podając jej nazwę, wykonane zostaną wtedy wszystkie polecenia, jakie jej przypiszemy.

Funkcje moga się znajdować w innym pliku, co uczyni nasz skrypt bardziej przejrzystym i wygodnym, tworzy się własne pliki nagłówkowe, wywołuje się je tak:

. ~/naszplik_z_funkcjami
nazwa_funkcji

Trzeba pamiętać o podaniu kropki + spacja przed nazwą pliku

TPUT

sposób na używanie kolorów w konsoli.

#!/bin/sh

clear

_GREEN=`tput setf 2`
_RED=`tput setf 4`
_BLUE=`tput setf 1`

echo "${_RED} na czerwono ${_GREEN} na zielono ${_BLUE} na niebiesko"

read czekaj
tput reset

echo "normal color"

Przydatne polecenia

Istnieje wiele przydatnych poleceń, które można wykorzystać przy pisaniu skryptów. Wymienie tu niektóre z nich.

cat – wyświetla plik. Opcje: -n – numeruje linie, -s – usuwa powtarzające się linie.
tac – cat w odwrotnej kolejności.
sum, cksum, md5sum – wyliczanie sumy kontrolnej pliku.
split – dzieli plik na kawałki o zadanej wielkości.
csplit – dzieli plik na części oddzielone wzorami określonymi przez użytkownika.
expand – konwertuje znaki tabulacji na spacje.
fmt – formatuje tekst.
logname – wyświetla nazwę użytkownika, jako który skrypt pracuje.
id – wyświetla dokładne informacje na temat użytkownika i grupy, jako które skrypt pracuje.
nl – czyta dane ze standardowego wejścia i na standardowe wyjście wyświetla te dane z numerowaniem linii.
hexdump – wyświetla otrzymane dane w postaci szesnastkowej.
od – to samo co wyżej, tylko wyświetla w postaci ósemkowej (między innymi – polecam manual)
printf – odpowiednik funkcji w C o tej samej nazwie – wyświetlanie danych w różnych formatach.
tsort – program do sortowania topologicznego, cokolwiek by to znaczyło.
tee – bardzo przydatny program. Dane, które dostanie na standardowe wejście wysyła spowrotem na standardowe wyjście, ale przy okazji zapisuje do podanego pliku.
mktemp – program służący do wygenerowania unikalnej nazwy pliku.
tr – zamienia jedne literki na inne, np. tr A-Za-z N-ZA-Mn-za-m spowoduje zaszyfrowanie tekstu podanego na standardowe wejście systemem ROT13 – jeden z prostszych szyfrów powodujących dodanie 13 do kodu ASCII każdego znaku. tr można też wykorzystać do drobnych poprawek w tekscie.
cut – kolejny bardzo przydatny program. Służy do obcinania podanych danych o podaną ilość znaków, słów, linii czy innych pól rozgraniczonych podanym znakiem. Np. cut -f 1 --delimiter=: /etc/passwd spowoduje wyświetlenie wszystkich nazw użytkowników (i tylko tych nazw) z pliku /etc/passwd.
tty – wyświetla nazwę urządzenia – aktualnej konsoli. Przydatne np. do uruchamiania różnych programów w zależności od tego, na której konsoli skrypt jest uruchamiany (np. czy zdalnej, lokalnej, a możne na terminalu szeregowym).
wc - program służący do zliczania ilości znaków, słów i linii z podanych danych.
sed – to już jest potężne narzędzie. W zasadzie to jest to już język programowania służący do obróbki danych tekstowych. Polecam man sed.
**find
 – też często się przydaje przy pisaniu skryptów. Jeśli ktoś jeszcze nie wie, to jest to program do wyszukiwania plików o podanych właściwościach. Polecam man find, ponieważ jest to program o dużej liczbie opcji.
**gawk
 – kolejny język programowania. Sporo potężniejszy od sed'a. Dużo dokumentacji jest w /usr/doc/gawk*/.

Przydatne wycinki

menu

#!/bin/bash
#
# menu.sh
#
# Zastosowanie polecenia select do tworzenia menu.

# Tekst objasnienia
PS3="Wybierz opcje: "

# Stworzenie menu
clear
# Naglowek menu z jakas informacja
echo
echo "          MENU PROGRAMU ZABAWA"
echo

select OPTION in "Jeden" "Dwa" "Trzy" "Break" "Exit"
do
  # Komunikaty kontrolne
  echo "$0: Zmienna REPLY zawiera ${REPLY}."
  echo "$0: Zmienna sterujaca OPTION zawiera ${OPTION}."
  #
  # Tekst wpisany przez uzytkownika siedzi w zmiennej REPLY
  #
  case ${REPLY} in
    1) echo "Opcja numer jeden." ;;
    2) echo "Opcja numer dwa." ;;
    3) echo "Opcja numer trzy." ;;
    4) break ;;
    5) exit 0 ;;
    # Dzialanie domyslne
    *) echo "Nie trafiles w klawisz?"
  esac

  # Zatrzymujemy obraz, aby nie zniknal
  echo "Naciśnij [ Enter ], aby kontynuować..."

  read

  clear
  # Naglowek menu z jakas informacja
  # (dla drugiego i nastepnych wyswietlen)
  echo
  echo "          MENU PROGRAMU ZABAWA"
  echo
done

# Komunikat kontrolny
echo "$0: Wychodzimy z petli select przez break."
exit 0

Przydatne linki

http://www.ournet.pl/…ha/main.html

Twoja ocena: Brak Średnio: 5 (6 głosów)

Dodaj nową odpowiedź

Informacja:

Osoby zamieszczające wypowiedzi naruszające prawo lub prawem chronione dobra osób trzecich, mogą ponieść z tego tytułu odpowiedzialność karną lub cywilną. Upewnij się, że twoja wypowiedź nie godzi w niczyje mienie.

  • Internal paths in single or double quotes, written as "internal:node/99", for example, are replaced with the appropriate absolute URL or path. Paths to files in single or double quotes, written as "files:somefile.ext", for example, are replaced with the appropriate URL that can be used to download the file.
  • Adresy internetowe są automatycznie zamieniane w odnośniki, które można kliknąć.
  • Dozwolone znaczniki HTML: <strong> <blockquote> <code>
  • Znaki końca linii i akapitu dodawane są automatycznie.

Więcej informacji na temat formatowania

Łapirobot
Proszę odpowiedzieć
b
i
Z
z
s
a
j
4
Z
j
Enter the code without spaces and pay attention to upper/lower case.