Narzędzia użytkownika

Narzędzia witryny


Pasek boczny

so:shell_constructs

Wykonywanie poleceń z pliku

Tryb (nie)interaktywnym

Powłoka uruchomiona bez argumentów działa w trybie interaktywnym.
Powłoka uruchomiona z nazwą pliku jako argumentem działa w trybie nieinteraktywnym.

W trybie interaktywnym powłoka czyta polecenia ze standardowego wejścia, w trybie nieinteraktywnym z podanego pliku.
W trybie nieinteraktywnym powłoka nie wypisuje na ekran znaku zachęty i niektórych komunikatów diagnostycznych.

Zadanie 1 Umieść w pliku plik w osobnych liniach polecenie echo hello world oraz polecenie date. Wykonaj sh plik.

Zadanie 2 Umieść w pliku plik polecenie readlink /proc/$$/exe. Ustaw uprawnienia wykonywania dla tego pliku (np. chmod +x plik). Wykonaj ten plik przez ./plik. Potem uruchom inną powłokę (np. csh lub zsh) i znów wykonaj ./plik. Jaka powłoka wykonuje domyślnie skrypty?

Shebang

Jeżeli pierwsza linia pliku plik zaczyna się od #!prog, to uruchamiając plik arg... zostanie wykonane prog plik arg....
Taka pierwsza linia ma nazwę shebang (i jest respektowana zarówno przez powłoki, jak i np. jądro Linuksa).
Shebang musi zaczynać się od pełnej ścieżki do pliku wykonywalnego i powinien móc zawierać opcje.
Jeśli plik plik zaczyna się od #!prog a1..., to wykonanie plik a2... uruchomi prog a1... plik a2....

Zadanie 3 Wpisz do pliku plik kolejno:
      • #!/bin/ls -la
      • #!/usr/bin/bat
      • #!/usr/bin/env cowsay
      • #!/bin/rm
Nadaj uprawnienia wykonania dla pliku i wykonaj go (z każdym kolejnym shebangiem).

Skrypty powłoki zawierają zwykle shebang #!/bin/sh (lub #!/bin/bash, jeśli skrypt korzysta z rozszerzeń Basha) i tradycyjnie nadaje im się rozszerzenie .sh.
W wielu językach skryptowych tradycyjnie używa się shebanga (np. python, perl, ruby).

Zadanie 4 Napisz skrypt hello_world.sh który po uruchomieniu wypisze na ekranie hello world.

Komentarze w skryptach

Komentarze zaczynają się od # (znajdującego się poza cudzysłowami, niepoprzedzonego odwróconym ukośnikiem, etc.).

# to jest komentarz
echo to nie jest komentarz # a to już jest
echo to \# "też # nie jest" ${0#bash} komentarz

Parametry pozycyjne (argumenty) i specjalne

Parametry to (omawiane wcześniej) zmienne oraz parametry pozycyjne i specjalne.
Parametry pozycyjne i specjalne można rozumieć jako specjalne zmienne które można tylko odczytywać i których nazwy zaczynają się od czegoś innego niż litera bądź podkreślnik.

Parametry pozycyjne

Parametry pozycyjne to argumenty wykonania skryptu lub funkcji.

Do pierwszych dziewięciu można odnosić się przez:
$1, $2, ..., $9
kolejne muszą używać notacji z nawiasami wąsatymi:
${1}, ${2}, …, ${9}, ${10}, ${11}, ${12}, …, ${42}, …

Przykłady

Komendą shift można usunąć pierwszy parametr i zmniejszyć pozostałym numery o jeden.

Dokumentacja Basha

Zadanie 5 Napisz skrypt który wyświetli drugi i czwarty argument.

Parametry specjalne

Odnoszące się do parametry pozycyjnych:

$# ilość parametrów pozycyjnych
$*
$@
parametry pozycyjne poddane dzieleniu na tokeny
"$*" parametry pozycyjne sklejone w jeden token
"$@" parametry pozycyjne, każdy osobnym tokenem

Przykłady

Pozostałe:

$0 w skrypcie nazwa uruchamianego skryptu; w innym razie nazwa powłoki
$$ identyfikator procesu powłoki
$? kod wyjścia ostatnio uruchomionej komendy
$! identyfikator ostatnio przeniesionego w tło procesu
$- ustawione opcje powłoki (patrz: man set)

Przykłady

Dokumentacja Basha

Zadanie 6 Napisz skrypt który wyświetli w kolejnych liniach:
      • wartość parametru $0,
      • swój PID,
      • ilość argumentów,
      • listę argumentów.

Zadanie 7 Uruchom skrypt z poprzedniego zadania podając w różny sposób ścieżkę do niego.

Zadanie 8 Wykorzystując komendę readlink -f lub realpath (które dostając jako argument ścieżkę do pliku zwracają jego bezwzględną ścieżkę po rozwinięciu symlinków) napisz skrypt który wyświetli ścieżkę do katalogu w którym się znajduje.

Wyrażenia warunkowe i pętle

if

Instrukcja warunkowa ma składnię:

if polecenie1
then
    kod wykonany jeśli polecenie1 zwróciło prawdę (wartość 0)
elif polecenie2
then
    kod wykonany jeśli polecenie2 zwróciło prawdę
else
    kod wykonany jeśli polecenie1 i polecenie2 nie zwróciło prawdy
fi
Instrukcje if i elif sprawdzają wartość zwracaną przez polecenie (a nie tekst wypisywany na standardowe wyjście czy wartość zmiennej).
Gałęzie elif i else są opcjonalne, elif może pojawiać się wielokrotnie.

Przykład bez używania znaków nowej linii:

if grep -q ^user: /etc/passwd; then echo "'user' present"; elif grep -q ^student: /etc/passwd; then echo "'user' absent, 'student' present"; else echo "neither present"; fi

Zadanie 9 Napisz skrypt który spróbuje usunąć plik o nazwie plik i jeśli usunięcie pliku się powiedzie, to wyświetli napis udało się, a jeśli nie to wyświetli nie udało się.

Zadanie 10 Zmień skrypt tak, by zamiast pliku o nazwie plik usuwał plik podany jako argument skryptu.

test

Polecenie test pozwala wykonywać m. inn. porównania zmiennych czy sprawdzać istnienie / rodzaj pliku.
Poza nazwą test to polecenie można wywołać przez [.
Jedyna różnica między test i [ polega na tym, że używając [ trzeba dodać na koniec ].
Np. test "ala" != "ala" jest identyczne z [ "ala" != "ala" ].

Testy łańcuchów znaków

Wyrażenie Kiedy prawdziwe
[ "$X" ]
[ -n "$X" ]
"$X" ma niezerową długość
[ -z "$X" ] "$X" jest puste (ma zerową długość)
[ "$X" = "$Y" ]Łańcuchy tekstu "$X" i "$Y" są identyczne
[ "$X" != "$Y" ]Łańcuchy tekstu "$X" i "$Y" są różne

Uwaga, pułapka: wyrażenie [ $X = $Y ] będzie działać jeśli $X i $Y rozwiną się każde do jednego słowa, ale jeśli któreś będzie puste bądź będzie zawierać spację, to spowoduje błąd składniowy.

Testy arytmetyczne

Wyrażenie Kiedy prawdziwe
[ "$X" -eq "$Y" ]liczba "$X" jest równa "$Y"
[ "$X" -ne "$Y" ]liczba "$X" jest różna "$Y"
[ "$X" -lt "$Y" ]liczba "$X" jest mniejsza "$Y"
[ "$X" -le "$Y" ]liczba "$X" jest mniejsza bądź równa "$Y"
[ "$X" -gt "$Y" ]liczba "$X" jest większa "$Y"
[ "$X" -ge "$Y" ]liczba "$X" jest większa bądź równa "$Y"

Testy plików (wybór)

Wyrażenie Prawdziwe kiedy plik "$X" istnieje i…
[ -e "$X" ] (po prostu istnieje)
[ -s "$X" ]nie jest pusty
[ -f "$X" ]
[ -d "$X" ]
jest zwykłym plikiem
jest katalogiem
[ -r "$X" ]
[ -w "$X" ]
[ -x "$X" ]
użytkownik może odczytywać plik
użytkownik może zapisywać do pliku
użytkownik może wykonywać plik

Negowanie, grupowanie

! arg neguje arg.
arg1 -a arg2 oznacza arg1 oraz arg2
arg1 -o arg2 oznacza arg1 lub arg2
nawiasy okrągłe ( … ) pozwalają grupować, ale trzeba je escape'ować żeby bash nie traktował ich jako znaki specjalne.

Przykład:

test \( -z "$X" -o "$X" -le 4 \) -a ! -s "$Y" -a \( "$Z" -le 2 -o "$Z" -ge 12 \)

Zadania

Zadanie 11 Napisz skrypt który sprawdzi czy został wywołany z dokładnie z dwoma argumentami i czy pierwszy z nich jest (arytmetycznie) mniejszy od drugiego. Jeśli nie, wyświetl właściwy komunikat o błędzie i wyjdź ze skryptu (komendą exit).

Zadanie 12 Napisz skrypt który sprawdzi czy pierwszy argument jest zwykłym plikiem do którego bieżący użytkownik ma prawa zapisu. Jeśli nie, wyświetl odpowiedni komunikat i wyjdź. Jeśli tak, dopisz bieżącą datę do tego pliku.

Zadanie 13 Napisz skrypt który sprawdzi czy pierwsza linia pliku (podanego jako argument) ma treść #!/bin/sh i czy plik jest wykonywalny. Jeśli tak jest, wyświetl nazwa pliku jest wykonywalnym skryptem powłoki.

Zadanie 14 Zmodyfikuj powyższy skrypt tak, by w razie potrzeby nadawał prawa dostępu i dopisywał shebang do wskazanego pliku.

[[ … ]] i (( … )) – rozszerzenie Basha

Bash wprowadza dwie dodatkowe konstrukcji powłoki do testów / porównań:

  • [[ … ]] jako konstrukcja działająca jak ulepszone polecenie test,
  • (( … )) do arytmetyki.

Wyrażenie arytmetyczne wewnątrz (( … )) jest wyliczane i jeśli zwróci 0, to (( … )) zwraca fałsz, w przeciwnym razie prawdę.
Wewnątrz (( … )) można używać m. inn. operatorów ==,!=, <, <=, >, >= i … ? … : ….

Działanie [[ … ]] jest dość złożone. Ponad to co umie komenda [, wyrażenie w podwójnych kwadratowych nawiasach:

  • [ dzieli wyniki podstawień na słowa, [[ zostawia je bez zmian, przykład
  • wewnątrz [[ nie są wykonywane dopasowania do nazw plików, przykład
  • Operator = i != dopasowują tekst do wzorca, a nie porównują teksty, przykład
  • Dodano operator =~ dopasowujący do wyrażenia regularnego, przykład
  • Operatory -a i -o zastąpiono przez && i ||, a nawiasów okrągłych nie trzeba escape'ować, np:
    if [[ -f file && ( ! -s file || ! -w file ) ]]; then echo "'file' exists and is not empty or not writeable"; fi

case

Instrukcja wyboru ma składnię:

case wartość in
  wzorzec1) polecenia1 ;;
  wzorzec2) polecenia2 ;;
  *) polecenia3 ;;
esac
Wartość jest kolejno dopasowywana do wzorzec1, wzorzec2, … i przy znalezieniu pierwszego dopasowania wykonywane są odpowiednie polecenia. Dalsze dopasowania nie są brane pod uwagę.
Polecenia są dowolnym ciągiem poleceń zakończonym ;;1).
Nie ma specjalnej składni na domyślne dopasowanie – używa się po prostu wzorca *.

Przykład bez używania znaków nowej linii:

case $(LANG= date "+%A") in Monday) echo "Ugh...";; S*day) tput bel; echo "Weekend!";; *) echo "a day.";; esac

Zadanie 15 Napisz skrypt który sprawdzi rozszerzenie pliku (podanego jako argument) i jeśli plik ma rozszerzenie:
      • pdf to wykona pdftotext plik -,
      • zip to wykona unzip -l plik,
      • jakiekolwiek inne, to wykona cat plik.

Pętle

Dokumentacja Basha

We wszystkich pętlach działają słowa kluczowe break i continue do, odpowiednio, wyjścia poza pętlę i rozpoczęcia następnego przebiegu pętli.

for

Pętla for ma składnię:

for zmienna in wartość1 wartość2   
do
   polecenia
done
W kolejnych przebiegach pętli kolejne wartości z listy są przypisywane do zmiennej zmienna. Po wyjściu z pętli zmienna ma ostatnią z przypisanych w pętli wartości.
Pominięcie in wartość1 wartość2 spowoduje przyjęcie za listę wartości "$@".

Często w miejsce listy wartości podaje się podstawienie które jest rozkładane na wiele wartości, np:
for NUM in {1..3} {7..9}for NUM in 1 2 3 7 8 9
for NUM in $(seq -w 001 3)for NUM in 001 002 003
for USERNAME in $(getent passwd | cut -f1 -d:)for USERNAME in root student ...
for FILE in $(grep -il 'ldap' /etc/*.conf 2>/dev/null)for FILE in /etc/autofs.conf /etc/ldap.conf ...2)

Przykład bez używania znaków nowej linii:

for X in {1..3}; do echo -n "Przebieg $X: "; date +%N; done

Zadanie 16 Napisz skrypt, który dla każdego pliku podanego jako argument wyświetli: jego nazwę, jego pierwszą linię i jego ostatnią linię.

Zadanie 17 Napisz pętlę która wyświetli wszystkie potęgi dwójki o wykładnikach od 1 do wybranej wartości.
(Wartość potęgi można wyliczyć np. wyrażeniem $((2**wykładnik)).)

for – rozszerzenie Basha

Bash wspiera też składnię for wyglądającą i działającą jak w C:

for ((i=0; i<5; i++))
do
   polecenia
done

Przykład bez używania znaków nowej linii:

for ((i=3;i<=15;i+=3)); do for ((j=0;j<i;j++)); do echo -n '*'; done; echo; done

while i until

Pętla ''while'' ma składnię:

while polecenie1  
do
   polecenia2
done
Bliźniacza pętla until ma składnię:
until polecenie1  
do
   polecenia2
done
Pętla while wykonuje polecenia2 do momentu aż polecenie1 zwraca prawdę.
Pętla until wykonuje polecenia2 do momentu aż polecenie1 zwraca fałsz.

Przykład bez używania znaków nowej linii:

X=7; until [ $X -eq 1 ]; do [ $((X%2)) -eq 1 ] && X=$((3*X+1)) || X=$((X/2)); echo $X; done

Zadanie 18 Napisz pętlę która wyświetli wszystkie potęgi dwójki mniejsze niż podana liczba.

Zadanie 19 Napisz skrypt który utworzy nowy plik myProg_NUM.log o najniższej możliwej wartości NUM, a następnie przypisze jego nazwę do zmiennej LOG i umieści w tym pliku bieżącą datę.

Wczytywanie wartości podawanych przez użytkownika

read

Komenda read [zmienna]... wczytuje jedną linię ze standardowego wejścia, a następnie:

  • (rozszerzenie Basha) jeżeli nie ma argumentów, read wpisuje linię do zmiennej REPLY,
  • read dzieli wczytaną linię na słowa,
  • każdej kolejnej zmiennej (podanej w liście argumentów) przypisuje kolejne słowo,
  • jeśli słów jest mniej niż zmiennych, pozostałe zmienne są ustawiane na pustą wartość,
  • jeśli słów jest więcej niż zmiennych, pozostałe słowa są doklejane do ostatniej zmiennej.

Przykład

Komenda read jest często wykorzystywana we wzorcach:
polecenie | while read X
do
  ...
done

1. polecenie jest wykonywane współbieżnie z pętlą
2. wyniki polecenia mogą mieć spacje w linii

while read X Y
do
  ...
done << EOF
wartoscX1 wartoscY1
wartoscX2 wartoscY2
EOF

ustawia się po parę/trójkę/… zmiennych naraz

Do poprawnego działania należy zapewnić żeby nic we wnętrzu pętli nie czytało ze standardowego wejścia

Zadanie 20 Napisz skrypt który dopisuje do pliku o nazwie log linię tekstu wczytaną ze standardowego wejścia poprzedzoną bieżącą datą.

Zadanie 21 Napisz skrypt który wczytuje liczby (znajdujące się w osobnych liniach) do momentu aż nie pojawi się pusta linia i następnie wyświetla najmniejszą z wczytanych liczb.

select – rozszerzenie Basha

Pętla select ma składnię identyczną jak pętla for:

select ZMIENNA in wartość1 wartość2   
do
   polecenia
done
Ta pętla:

  1. wyświetla numerowaną listę wartości,
  2. czeka na wpisanie linii wejścia przez użytkownika,
  3. przeczytaną linię przypisuje zmiennej REPLY
  4. jeśli linia jest jednym z numerów, do ZMIENNA przypisuje odpowiadającą wartość
  5. wykonuje polecenia
  6. wraca do kroku 2

Zadanie 22 Przeanalizuj i przetestuj poniższy kod. Zmień go tak, by dopiero wpisanie q przerywało pętlę.

select FILE in /etc/logrotate.d/*
do
  [[ $FILE ]] || continue
  echo "===$FILE==="
  cat "$FILE"
  break
done
1) Bash rozszerza składnię o ;& które przechodzi do komend z kolejnego wzorca i ;;& które dopasowuje kolejne wzorce.
2) Uwaga na spacje w nazwach plików! Ta wersja nie jest na nie odporna.
so/shell_constructs.txt · ostatnio zmienione: 2023/05/15 15:04 przez jkonczak