Scott Tiger Tech Blog

Blog technologiczny firmy Scott Tiger S.A.

VN:F [1.9.22_1171]
Rating: 5.0/5 (2 votes cast)

Bash

Spis treści
1. Wstęp
2. Historia
3. Skróty klawiaturowe
4. cd: przejście pomiędzy katalogami
5. Historia wiersza poleceń
6. Etapy procedury przetwarzania polecenia (uproszczone)
7. Strumień wyjściowy
8. Strumień wejściowy
9. Polecenia
    9.1. Aliasy
    9.2. Zadania (ang. jobs)
10. Skrypty
    10.1. Wykonywanie poleceń
    10.2. Zmienne
        10.2.1. Tablice
    10.3. Wyrażenia arytmetyczne
    10.4. Konstrukcje składniowe
    10.5. Funkcje
    10.6. Sygnały
11. Konfiguracja
    11.1. Pliki startowe
    11.2. Ciekawe zmienne środowiskowe
    11.3. Prompty
    11.4. readline
    11.5. shopt
    11.6. set
    11.7. Inne
12. Zadania administracyjne
13. Bezpieczeństwo
14. Często popełniane błędy
15. Przenośność skryptów
    15.1. Różnice między Bashem a Sh
16. Inne
17. Literatura

Wstęp

Wiersz poleceń (w praktyce: Bash) jest jednym z najczęściej używanych przez programistów narzędzi Linuksa, zwłaszcza w połączeniu z SSH.

Notatki są przeznaczone raczej dla średniozaawansowanych użytkowników Basha, pochodzą ze szkolenia z dnia 11.01.2010 (opartego głównie na książce wymienionej w bibliografii) i zostały uzupełnione w póżniejszym terminie notatkami z lektury manuala.

Historia

Drzewko „dziedziczności” powłok przedstawia poniższy rysunek.

  sh Powłoka Bourne'a (System V)
    ksh Powłoka Korne'a
      tksh Tcl + Korn
      zsh
    bash Bourne Again Shell
  csh Powłoka C (BSD)
    tcsh

Zaawansowani użytkownicy Linuxa często cenią sobie wysoko powłokę zsh.

Skróty klawiaturowe

Skróty klawiaturowe w Bashu są zaczerpnięte z edytora Emacs (często zamiast Esc można użyć lewego Alt, jeśli window manager (KDE, GNOME), na to pozwala). Niektóre polecenia można wywołać a argumentem numerycznym (który zwykle oznacza wielokrotne wykonanie polecenia) – wtedy należy poprzedzić polecenie liczbą wprowadzoną z wciśniętym klawiszem Alt.

Najciekawsze polecenia:

Ctrl+K Usunięcie wszystkich znaków do końca wiersza Ctrl+U Usunięcie wszystkich znaków do początku wiersza
Ctrl+L Wyczyszczenie ekranu i powtórne wyświetlenie wiersza Ctrl+O To samo co naciśnięcie Enter i wyświetlenie kolejnego wiersza z historii
Ctrl+R Wyszukiwanie przyrostowe wstecz w historii Ctrl+S Wyszukiwanie przyrostowe w przód w historii
Ctrl+T Zamiana pozycji dwóch znaków Esc+T Zamiana dwóch słów miejscami
Ctrl+V Dosłowne zapisanie kolejnego wprowadzanego znaku Ctrl+X Ctrl+V Wyświetlenie informacji o wersji Basha
Ctrl+_ Undo Esc+D Usunięcie słowa przed kursorem
Esc+B Przesunięcie kursora o jedno słowo wstecz Esc+F Przesunięcie kursora o jedno słowo w przód
Esc+L Zamiana liter w słowie przed kursorem na małe Esc+U Zamiana liter w słowie przed kursorem na duże
Esc+< Przejście do pierwszego wiersza pliku historii Esc+> Przejście do ostatniego wiersza pliku historii
Esc+. Wstawienie ostatniego słowa poprzedniego polecenia; z argumentem n: wstawia n-te słowo poprzedniego polecenia Esc+C Zmiana słowa znajdującego się przed kursorem tak, aby zaczynało się wielką literą
Esc+Tab Próba uzupełnienia słowa danymi zapisanymi w wierszu poleceń Ctrl+X e Wykonanie ostatnio zdefiniowanego makra klawiatury
Ctrl+X ( Rozpoczęcie definicji makra Ctrl+X ( Zakończenie definicji makra
Esc+Ctrl+E Podmiana wartości aliasu, polecenia historycznego i podstawienie wartości w bieżącym wierszu Ctrl+D (gdy wiersz wprowadzania pusty) – wyjście z shella

cd: przejście pomiędzy katalogami

export CDPATH=.:..:../..:~/:~/katalog_dowiazan
Zmienna $CDPATH przechowuje listę rozdzielanych znakami dwukropka nazw katalogów, które są przeszukiwane przez wbudowane polecenie cd. Pełni ona taką samą funkcję dla instrukcji cd, jak zmienna $PATH dla uruchamianych skryptów. W przykładzie często używane nazwy katalogów w wartości zmiennej; katalog_dowiązań oznacza katalog przechowujący jedynie dowiązania symboliczne do innych często wykonywanych katalogów
cd
przejście do katalogu domowego
cd ~piotrek
przechodzi do katalogu domowego użytkownika piotrek
cd -
przejście do poprzedniego katalogu (sprzed poprzedniego wykonania cd)
pushd nowy_katalog
zapisanie dotychczasowego katalogu na stosie i przejście do nowego katalogu
dirs -v
wyświetla aktualny stos katalogów
pushd +2
przeniesienie na szczyt stosu wpisu o numerze 2 i jednoczesna zmiana bieżącego katalogu na odpowiadający temu wpisowi

Historia wiersza poleceń

!-4
powtórz czwarte polecenie od końca
!string
powtórz najświeższe polecenie rozpoczynające się łańcuchem string
!?string?
powtórz najświeższe polecenie zawierające (gdziekolwiek) łańcuch string
^napis1^napis2^
powtórz ostatnie polecenie z podmienionym napis1 na napis2
!20:p
tylko wyświetla polecenie o nrze 20, bez wykonywania go
!:1
pierwszy argument ostatniego polecenia
!$
ostatni argument ostatniego polecenia (to samo co !:$, patrz także skrót klawiaturowy Esc+.)
!*
wszystkie argumenty poprzedniego polecenia
history -c
czyści całą historię
HISTSIZE
liczba przechowywanych (w pamięci) wierszy historii (domyślnie 500)
HISTFILESIZE
liczba przechowywanych w pliku ~/.bash_history wierszy historii (domyślnie 500); gdy interaktywna sesja Basha kończy pracę, $HISTSIZE wierszy historii jest kopiowanych do pliku (lub dopisywanych na końcu jeśli ustawiona jest opcja histappend), po czym plik jest przycinany do HISTFILESIZE wierszy
shopt -s histappend
wymusza dopisywanie historii użytych poleceń na końcu pliku historii przy kończeniu pracy interaktywnej sesji Basha
export HISTTIMEFORMAT='%Y-%m-%d_%H:%M:%S '
(od Basha 3.0) powoduje że wraz z poleceniem w historii będzie pamiętany (i zapisywany do pliku historii) czas jego wykonania; polecenie history wyświetli czas wydania każdego polecenia w podanym formacie

Etapy procedury przetwarzania polecenia (uproszczone)

  1. Podział na składowe rozdzielone znakami ; ( ) <> | &
  2. Rozwijanie aliasu
  3. Podstawienie tyldy
  4. Podstawienie wartości zmiennych
  5. Podstawienie wyniku polecenia (odwrotne ciapy)
  6. Podstawienie wyniku wyrażenia arytmetycznego
  7. Podział na słowa (separatorem jest spacja)
  8. Rozwinięcie ścieżek dostępu (*, ?, itp.)
  9. Odszukanie i wykonanie polecenia

W podwójnych ciapach wykonywane są kroki 4, 5 i 6. Tekst ujęty w pojedyncze ciapy opiera się wszystkim przekształceniom.

Przykład:

Kawałek skryptu:

  STRONA="ls | less"
  $STRONA

Wyświetli komunikat błędu:

  ls: |: No such file or directory
  ls: less: No such file or directory

Ale jeśli napiszemy:

  STRONA="ls | less"
  eval $STRONA

wówczas uzyskamy pożądany rezultat (stronicowany listing bieżącego katalogu).

Strumień wyjściowy

2>&1 === >& === &>

{ pwd; ls; cd ../gdzies; pwd; ls; } > /tmp/wszystko.txt
Nawiasy klamrowe muszą być otoczone znakami spacji, a na końcu ostatniego grupowanego polecenia musi być średnik. Opcjonalnie można wykorzystać znaki nawiasu okrągłego, nakazując interpreterowi wykonanie poleceń w podpowłoce.
echo opcja $OPTSTR >&5
wypisanie wyniku na 5ty deskryptor
skrypt 3>&1 1>&2 2>&3-
zamienia deskryptory 1 i 2 przy użyciu pomocniczego deskryptora 3 (usuwanego potem)
skrypt 3>&1 1>dziennik.stdout 2>&3- | tee -a dziennik.stderr
zamienia stderr i stdout tak aby można było kierować dane ze strumienia stdout do pliku dziennika, a informacje ze strumienia stderr zarówno na ekran jak i do pliku
>&-
zamknięcie stdout

stdout jest buforowany, stderr nie (komunikaty o błędach widoczne są natychmiast)

Kolejność operacji przekierowania jest bardzo istotna. Porównajmy dwa poniższe polecenia:

  polecenie >plik 2>&1
  polecenie 2>&1 >plik

W pierwszym przypadku stdout jest skierowany do pliku, a stderr w to samo miejsce co stdout – wszystkie dane wynikowe zostaną więc zapisane do pliku. W drugim przypadku rezultat jest inny: stderr jest skierowany do stdout (który na tym etapie działania instrukcji odnosi się do ekranu!), a następnie stdout zostaje skierowany do pliku. W efekcie tylko stdout zostanie skierowany do pliku, a stderr będzie wyświetlany na ekranie.

Jednak kolejność ta musi być odwrócona w zadaniach bazujących na mechanizmie potoku. Nie można przecież umieścić operatora przekierowania za symbolem potoku, ponieważ za operatorem potoku zapisane jest następne polecenie. Powłoka czyni więc wyjątek podczas realizacji instrukcji:

  polecenie 2>&1 | innepolecenie

(począwszy od Bash 4 można napisać polecenie |& innepolecenie).

Efektem ubocznym takiego sposobu działania jest to że nie ma możliwości przekazania do potoku jedynie stderr – przynajmniej jeśli nie zamieni się uprzednio deskryptorów plików.

Przekierowanie stderr na czas działania bloku instrukcji:

  # zachowanie starego deskryptora stderr
  exec 3>&2
  # skierowanie stderr do pliku dziennika
  exec 2> plik_logow.txt

  # tutaj instrukcje, ktorych strumien stderr zostal globalnie przekierowany do pliku

  # przywrocenie standardowego strumienia stderr i zamkniecie deskryptora 3
  exec 2>&3-

Strumień wejściowy

Sposób na umieszczanie łańcuchów wielowierszowych w skrypcie:

  grep $1 <<'EOF'
  napisy
  EOF
0<&-
zamknięcie stdin
diff <(ls /etc/rc2.d) <(ls /etc/rc3.d)
przykład na wstawienie wyników poleceń tam gdzie program oczekuje nazw plików; sprawdź z ciekawości wynik polecenia echo <(/bin/true)

Polecenia

type identyfikator
określa czy identyfikator jest poleceniem wbudowanym, funkcją, aliasem, czy zewnętrznym programem; pokazuje również czy polecenie jest zhaszowane (bash najpierw sięga do hasza, a dopiero w następnej kolejności szuka w katalogach określonych w zmiennej PATH)
builtin echo
uruchamia polecenie wbudowane (bez względu na to czy istnieje funkcja, alias lub polecenie zewnętrzne o tej samej nazwie)
command echo
uruchamia polecenie zewnętrzne ignorując aliasy i funkcje o tej samej nazwie
echo
uruchamia na pewno nie alias

Aliasy

alias
wyświetla listę wszystkich zdefiniowanych aliasów
alias terms='watch -n 1 w -hs'
tworzy alias (bardzo ważne są pojedyncze cudzysłowy); podstawienie wykonywane jest tylko raz więc nie ma obawy o nieskończone cykle
unalias ll
usuwa definicję aliasu

Doświadczenie uczy, że opracowując skrypt nie powinno się polegać na aliasach. Aliasy nie obsługują parametrów ($1 itp.)

Zadania (ang. jobs)

Każde polecenie na czas wykonania otrzymuje od Basha swój numerek porządkowy (wyświetlany na konsoli gdy zlecimy wykonanie polecenia w tle) do którego możemy potem się odwołać za pomocą notacji %n. Polecenie pierwszoplanowe możemy na ogół wstrzymać kombinacją klawiszy Ctrl+Z.

Proces uruchomiony w tle który próbuje czytać z terminala lub pisać na terminal, dostaje sygnał SIGTTIN (zwykle usypia proces).

bg %1
wznawia wykonanie pierwszego zadania w tle
fg %1
wznawia wykonanie pierwszego zadania na pierwszym planie
jobs
wyświetla listę zadań (procesów wykonywanych przez Basha na rzecz użytkownika)
disown -h %1
powoduje że przy kończeniu pracy shella do joba %1 nie będzie wysyłany sygnał SIGHUP.

Skrypty

Wykonywanie poleceń

  first; second; third # sekwencyjne, kod błędu bez znaczenia
  first && second && third # sekwencyjne, następne wtw gdy poprzednie OK
  first & second & third # równoległe
  • Zapis $(pwd) jest bardziej uniwersalną odmianą `pwd` (konstrukcje $(...) można np. zagnieżdżać)

Warto stosować operator && zamiast if:

  cd tymczasowy
  if (( $? )); then rm *; fi # sprawdź kod błędu

jest tym samym co:

  cd tymczasowy && rm *

Użycie ||:

  polecenie || printf "%b" "Polecenie zakończone błędem, radź sobie samn"
  polecenie || { printf "%b" "Błądn"; exit 1; }

Zmienne

  • $FN zapisujemy również jako ${FN}
  • Jeśli parametr zawiera spacje, to lepiej jest pisać „${1}” niż ${1}
$*
tablica parametrów skryptu
"$@"
tablica parametrów skryptu – każdy ujęty w cudzysłowy (na wypadek gdyby zawierały znaki spacji)
${#}
liczba parametrów skryptu
${#VAR}
długość ciągu przechowywanego w zmiennej VAR
  for FN in "$@"
  do
    ls -al "$FN"
  done
${1:-"/tmp"}
parametr 1, a jeśli nie ma lub wartość pusta to „/tmp”
${HOME:="$(pwd)"}
nadaj wartość jeśli nie ma lub pusta, zwróć wartość zmiennej
${HOME="/tmp"}
nadaj wartość tylko wtedy gdy zmienna jest niezdefiniowana, zwróć wartość zmiennej
${NAZWA:3:10}
zwraca podciąg wartości zmiennej NAZWA o długości 10 znaków począwszy od pozycji 3 (pozycje numerujemy od zera)
${FOO^}
wartość zmiennej gdzie pierwsza litera będzie duża (^^ oznacza wszystkie duże, przecinek oznacza pierwsza mała, dwa przecinki – wszystkie małe) – od Bash 4.0
FILEDIR=${1:?"Błąd. Musisz określić katalog początkowy"}
rzuca błąd gdy nie zmienna nie zdefiniowana
mv "${FN}" "${FN/.txt/.html}"
zmień nazwy plików z *.txt na *.html

Tablice

Zmienna w Bashu może również przechowywać tablicę (indeksowaną liczbami naturalnymi) oraz tablicę asocjacyjną (indeksowaną łańcuchami, począwszy od Bash 4).

my_arr=(misio pysio)
inicjuje tablicę dwoma elementami
panstwa=([pl]=Polska [en]="Wielka Brytania")
inicjuje mapę dwoma parami
${panstwa[pl]}
odwołanie do elementu mapy (elementy tablicy indeksujemy od 0)
${my_arr[@]}
łańcuch zawierający elementy tablicy rozdzielone spacjami i ujęte w cudzysłowy
$my_arr
pierwszy element tablicy
${#my_arr[0]}
liczba znaków pierwszego elementu tablicy
${#my_arr[@]}
liczba elementów tablicy
unset my_arr[1]
usuwa drugi element tablicy
mapfile TABLICA
wczytuje wiersze z stdin do wskazanej tablicy

Wyrażenia arytmetyczne

  let COUNT+=5 # nie może być spacji wewnątrz wyrażenia, nie ma $ przed nazwą zmiennej (z wyj. $1)
  COUNT=$(( COUNT + 5 ))

Bash nie umożliwia wykonywania obliczeń z wykorzystaniem liczb zmiennoprzecinkowych (w odróżnieniu od Ksh i Zsh).

Konstrukcje składniowe

Warto wyrobić sobie nawyk dodawania białych spacji pomiędzy poszczególnymi tokenami (zwłaszcza zawierającymi nawiasy) – czasami są konieczne. Alternatywą dla średnika w poniższych konstrukcjach jest znak końca wiersza.

  if lista; then lista; elif lista; then lista; ... else lista; fi
  if (( $# = 3.0
  for (( i=0 ; i < 10; i++ )) ; do echo $i ; done
  for (( i=0, j=0 ; i+j < 10 ; i++, j++ )) ; do echo $i ; done
  for fp in $(seq 1.0 .01 1.1) ; do echo $fp ; done
  while (( COUNT < MAX ))
  do
    ...
    let COUNT++
  done
  until test_commands; do commands; done
  case $FN in
    *.gif) gif2png $FN
      ;;
    *.jpg) jpg2gif $FN
      ;;
    *) print "Format pliku %s nie jest obsługiwany" $FN
      ;;
  esac

Instrukcje break i continue pozwalają kontrolować przebieg wykonania pętli

Funkcje

  function usage() { ... } >&2
  function usage { ... }
  usage() { 
    local HIDN
    HIDN = 5
    ...
  }
  • Nie ma parametrów formalnych; w treści funkcji $1, $2, … przechowują wartości kolejnych parametrów funkcji
  • Jedyny sposób na zwrócenie sensownej wartości przez funkcję to przypisanie na zmienną globalną albo wypisanie na STDOUT (wynik funkcji można wtedy złapać w odwrotne ciapy)
  • Można używać rekurencji
FUNCNAME
ta zmienna przechowuje nazwę aktualnie wykonywanej funkcji ($FUNCNAME === $FUNCNAME[0], $FUNCNAME[1] zawiera kolejną nazwę funkcji na stosie)
caller 0
w treści funkcji wyświetla zawartość pierwszej ramki stosu (nazwa pliku, numer wiersza, nazwa funkcji)

Sygnały

trap 'echo "Nie pokonasz mnie!"' ABRT EXIT HUP INT TERM QUIT
przechwytywanie sygnałów (trap -l pokazuje listę sygnałów)
trap - INT QUIT
przywrócenie domyślnej obsługi sygnału
  • Kod zwracany po zakończeniu skryptu w wyniku otrzymania sygnału ma wartość 128+numer_sygnału.
  • Bash deklaruje kilka przydatnych pseudosygnałów. Przed każdym poleceniem rzucany jest sygnał DEBUG, sygnał RETURN jest generowany w chwili wznowienia wykonywania skryptu po wywołaniu funkcji, natomiast ERR jest przesyłany po błędnym zakończeniu pojedynczego polecenia. Pseudosygnał ERR nie jest generowany gdy jakieś polecenie shella zakończy się z błędem ale jest np. warunkiem obliczanym w instrukcji if

Konfiguracja

Pliki startowe

/etc/profile
ogólnosystemowy, wykonywany jako pierwszy skrypt startowy przy każdym uruchomieniu powłoki
~/.bashrc
dla interaktywnych powłok Bash
~/.bash_profile
dla powłok logowania
~/.bash_logout
wykonywany w chwili wylogowywania

Ciekawe zmienne środowiskowe

LINENO
liczba wykonanych przez powłokę poleceń
PROMPT_COMMAND
polecenie wykonywane przed każdorazowym wyświetleniem prompta
RANDOM
losowa liczba całkowita (kolejne sięgnięcie do tej zmiennej zwraca inną liczbę)
export LESS="--LONG-PROMPT --LINE-NUMBERS --QUIET"
domyślne parametry dla less, m.in. numerowanie wierszy w wyświetlanym pliku
TMOUT
jeśli użytkownik pracujący w trybie interaktywnym nie wprowadzi żadnego polecenia w czasie $TMOUT sekund, powłoka zakończy działanie
SECONDS
liczba sekund które upłynęły od czasu uruchomienia powłoki
BASH_LINENO, FUNCNAME, BASH_SOURCE
zmienne tablicowe przechowujące stos (odpowiednio numery wierszy, nazwy procedur, nazwy plików źródłowych)
PIPESTATUS
zmienna tablicowa zawierająca lista kodów wyjściowych poszczególnych procesów ostatniego potoku (pojedyncze polecenie też jest potokiem)

Prompty

export PS1='u@h A:w $ '
Nazwa użytkownika @ nazwa systemu, czas oraz ścieżka do bieżącego katalogu
export PS1='[e[0;31m]u@h A:w $[e[m] '
jw. ale na czerwono żeby wyraźnie odróżniać wyniki różnych poleceń
export PS1='n[u@h:Tl:L$SHLVL:C!:D{%Y-%m-%d_%H:%M:%S_%Z}]n$PWD$ '
Nazwa użytkownika @ nazwa systemu, nazwa terminala (T), poziom powłoki (L), numer polecenia (C) oraz data i czas w formacie ISO 8601

readline

Biblioteka GNU Readline leży u podłoża Basha i zajmuje się przetwarzaniem danych wejściowych. Zmieniając ustawienia biblioteki, możemy np. zmieniać mechanizm uzupełniania poleceń

~/.inputrc
plik konfiguracji readline

Przykładowy plik konfiguracyjny z ciekawymi ustawieniami:

# Aby ponownie odczytać plik (i uaktywnić zmiany), należy użyć instrukcji
# bind -f $SETTINGS/inputrc

# Uwzględnienie systemowych dyrektyw odwzorowania i instrukcji przypisania wartości zmiennym, które są
# zapisane w pliku /etc/inputrc
$include /etc/inputrc

$if Bash
  # Zignorowania informacji o wielkości liter podczas uzupełniania polecenia
    set completion-ignore-case on
  # Dołączenie znaku ukośnika do nazw katalogów (podczas uzupełniania nazwy)
    set mark-directories on
  # Wykorzystanie podczas uzupełniania polecenia instrukcji ls -F
    set visible-stats on
  # Cykliczna zmiana niejednoznacznych nazw zamiast wyświetlania listy
    "C-i": menu-complete
  # Włączenie słyszalnego dzwonka
    set bell-style audible

  # Makra użyteczne podczas pracy z powłoką
  # edycja zmiennej $PATH
    "C-xp": "PATH=${PATH}eC-eC-aefC-f"
  # przygotowanie do wpisania słowa otoczonego znakami cudzysłowu -- wstawienie
  # początkowego i końcowego znaku cudzysłowu i przesunięcie kursora za cudzysłów otwierający
    "C-x"": """C-b"
  # wstawienie znaku odwrotnego ukośnika (testowanie symboli specjalnych w sekwencjach znaków i makrach)
    "C-x\": "\"
  # Otoczenie znakami cudzysłowu bieżacego lub poprzednioego słowa
    "C-xq": "eb"ef""
$endif

shopt

shopt -s dirspell
tabulacja przy wprowadzaniu ścieżki do katalogu próbuje poprawiać pisownię katalogu jeśli użytkownik zrobił literówkę
shopt -q -s cdspell
automatyczna korekta prostych błędów w użyciu polecenia cd
shopt -s nocaseglob
podczas podmiany wartości ścieżek dostępu ignorowana jest wielkość liter
shopt -s globstar
od tej pory ** oznacza pełną listę plików i ścieżek poniżej bieżącego katalogu (począwszy od Bash 4)

set

$-
zmienna przechowująca bieżące ustawienia set
set -e
kończy pracę powłoki po zwróceniu przez jakiekolwiek zwykłe polecenie niezerowego kodu zakończenia
set -o vi
edycja poleceń w trybie Vi (zamiast domyślnego trybu Emacs)
set +h
powoduje że polecenia shella nie będą haszowane
set -u
powoduje traktowanie odwołań do nieistniejących zmiennych jako błąd
set -v
powoduje wypisywanie poleceń na stdout w miarę ich wykonywania

Inne

  • Polecana technika to utworzenie katalogu ~/bin, dodanie tego katalogu do zmiennej $PATH (na końcu!) i umieszczanie w tym katalogu wszystkich skryptów tworzonych przez użytkownika. Dzięki temu można ich używać tak jakby znajdowały się w /usr/bin.
  • Bash pozwala dynamicznie ładować polecenia napisane w języku C (moduły) tak aby nie uruchamiały one osobnych procesów, tzn. będą one działać na zasadzie poleceń wbudowanych w powłokę.
complete -A file -X '!*.@(Z|gz|tgz)' gunzip
powoduje że dla polecenia gunzip klawisz TAB będzie podpowiadał tylko pliki archiwów
echo {05..15}
wyświetla ciąg liczb 05 06 07 … 15 (począwszy od Bash 4 jednocyfrowe liczby w wynikach tego polecenia będą poprzedzone zerem).

Zadania administracyjne

screen
Patrz osobny artykuł: Screen
script plik
od tej pory wszystkie polecenia i ich wyniki będą dodatkowo zapisywane w pliku plik, aż do wykonania polecenia exit

Bezpieczeństwo

unalias -a
usuwa wszystkie aliasy (nawet jeśli jakiś chitrusek zrobił alias dla polecenia unalias)
hash -r
usuwa wszystkie wpisy w tablicy odwzorowań plików wykonywalnych (aby przyspieszyć kolejne wywołania poleceń, powłoka zapamiętuje położenia plików wykonywalnych najczęściej wykonywanych instrukcji; jeden ze sposobów oszukania użytkowników polega na zatruciu tablicy)
ulimit -H -c 0 --
uniemożliwia tworzenie plików core’a (w których mogą być np. hasła)

Często popełniane błędy

stty/reset/tset
przywraca poprawne ustawienia terminala (po np. próbie wyświetlenia pliku binarnego); reset i tset mogą być zbyt drastyczne, zwykle stty działa poprawnie
  • Wyrażenia regularne są wykorzystywane w powłoce bash tylko wraz z operatorem =~
  • W skryptach nie można stosować znaków końca wiersza Windows (dos2unix)
  • Nie nazywaj skryptu „test„, bo to jest polecenie wbudowane shella (ale np. wywołanie ./test jest OK).
  • Warto otaczać cudzysłowami wartości przypisywane zmiennym powłoki (na wypadek spacji)
ZMIENNA=misio pysio
przypisuje zmiennej wartość misio i wykonuje polecenie pysio (w następnym wierszu ZMIENNA nie będzie już istnieć)
ZMIENNA="misio pysio"
nadaje zmiennej wartość "misio pysio"

Przenośność skryptów

#!/usr/bin/env bash
bardziej przenośne niż #!/bin/bash
bash --posix
wymusza tryb działania zgodny ze standardem POSIX
command -p program parametry
odpowiada za wyszukanie programu zgodnie z zaleceniami POSIX (np. w Solarisie uwzględnione zostaną katalogi /usr/xpg4 i /usr/xpg6)
  • Aby skrypt był bardziej przenośny, nie należy używać długich nazw opcji (np. --field-separator, które są charakterystyczne dla oprogramowania GNU).
  • Korzystając z maszyn wirtualnych (np. VMWare) można szybko uzyskać środowisko Solarisa czy BSD żeby w nich przetestować działanie pisanych skryptów

Różnice między Bashem a Sh

Ze względu na przenośność, przy pisaniu skryptów warto obstawać przy standardzie jakim jest przodek Basha, czyli shell sh. Warto więc wiedzieć jakich konstrukcji nie ma w sh:

  • zmienne tablicowe
  • konstrukcje pushd, popd, dirs, builtin, command, printf, shopt, local, select, time, ((, [[
  • rozwijanie tyldy
  • aliasy
  • zaawansowane przetwarzanie wartości zmiennych (np. ${#ZMIENNA} do zwracania długości zmiennej)
  • konstrukcja $(...) (zamiast tego trzeba używać odwrotnych ciapów)
  • podstawianie procesu <(list)
  • operator przekierowania &>

Inne

  • Programy typu kdialog pozwalają na interakcję z użytkownikiem za pomocą prostych okienek GUI (np. wybór pliku, pytanie z odpowiedzią tak/nie, prośba o podanie łańcucha, komunikat itp.); kdialog jest dostępny razem z KDE, dialog wykorzystuje bibliotekę znakową NCurses, oprócz tego mamy też gtkdialog (pozwala zdefiniować wygląd okienka w XMLu, wykorzystuje GTK+), Xdialog, zenity, whiptail i xmessage.
    kdialog --msgbox "Message goes here."
  • Aby sprawdzić czy powłoka działa w trybie interaktywnym, można sprawdzić stan zmiennej środowiskowej PS1 (istnieje tylko w trybie interaktywnym) lub zmiennej $- (w trybie interaktywnym ma wartość i.
  • Jeśli Bash nie umie znaleźć funkcji/polecenia o danej nazwie, wówczas usiłuje wywołać funkcję command_not_found_handle która – jeśli istnieje – wykonuje się jak brakujące polecenie (z parametrów funkcji można odczytać nazwę oryginalnego polecenia i jego parametry)
  • Programy wiersza poleceń w UNIXie na ogół przestrzegają tradycji która mówi że uruchomienie programu bez opcji powinno być niedestrukcyjne, a jedynie informacyjne. Dzięki temu można bez obaw uruchomić polecenie żeby się zorientować do czego służy
  • Ściągnięcie strony WWW za pomocą shella (nie zadziała w Debianie, który ma inaczej skompilowanego Basha):
  exec 3 /dev/tcp/www.ippages.com/80
  echo -e "GET /simple/?se=1 HTTP/1.0n" >&3
  cat <&3
  • Kiedy w poleceniu pojawia się symbol !$, powłoka zastępuje go ostatnim członem poprzedniego polecenia.
chsh /bin/bash
powoduje że domyślną powłoką na bieżącym koncie UNIXowym będzie Bash; lista dopuszczalnych powłok jest podana w pliku /etc/shells
getopts
polecenie wbudowane shella do użycia przez skrypty do parsowania parametrów wywołania skryptu
mkdir -p ~/misio/pysio/{old,new,dist,bugs}
tworzy od razu wiele katalogów, opcja -p powoduje tworzenie katalogów misio i pysio jeśli jeszcze nie istnieją
getconf -a
wyświetla wartości różnych ograniczeń, np. max. długość polecenia w wierszu wprowadzania (ARG_MAX), max. długość loginu (LOGIN_NAME_MAX), itp.
find katalog -name '*z*' -print0 | xargs -0 program
rozwiązanie problemu ograniczenia długości wiersza wprowadzania; poszczególne argumenty będą rozdzielone znakiem NULL (nie może występować w nazwie pliku), polecenie program będzie wykonane raz lub więcej razy w zależności od ilości parametrów wygenerowanych przez find
logger -p local0.notice "moj komunikat"
pozwala zalogować komunikat do logu systemowego
nc
program NetCat pozwala logować komunikaty przez sieć
(cat tresc_wiadomosci; uuencode /plik_zalacznika nazwa_zalacznika) | mail -s "Temat" odbiorca1@host.pl odbiorca2@host.pl
wysyła maila z treścią i załącznikiem (działanie zależne dość mocno od konfiguracji)
date -d '+4 days' '+%a %Y-%m-%d %H:%M:%S %z'
wyświetla wskazaną datę w podanym formacie
nohup skrypt &
demonizowanie skryptu tak aby działał po wylogowaniu się użytkownika (patrz też screen)
source skrypt
wykonuje w tej samej powłoce polecenia zawarte we wskazanym skrypcie (zwykłe wywołanie skryptu uruchomiłoby go w podpowłoce)
time polecenie
wykonuje polecenie i dodatkowo wypisuje informacje o czasie jego wykonania
ulimit
pozwala odczytywać i ustawiać ograniczenia dla zasobów dostępnych dla procesów uruchamianych z Basha (np. max. rozmiar stosu, max. ilość czasu procesora, wątków, otwartych plików itp.); twardy limit po ustawieniu może być modyfikowany wyłącznie przez administratora; limit miękki może być co najwyżej tak wysoki jak twardy limit

Literatura

Bash, 5.0 out of 5 based on 2 ratings
Share and Enjoy:
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Śledzik
  • Blip
  • Blogger.com
  • Gadu-Gadu Live
  • LinkedIn
  • MySpace
  • Wykop

2 Komentarzy do “Bash”

  1. porto napisał(a):

    Świetny text , tego szukałem od dawna .. super wszystko zebrane w fajnej pigule ..

  2. Piotr napisał(a):

    Przy tablicach asocjacyjnych warto by było dodać o wcześniejszym użyciu polecenia: declare -A nazwa_tablicy. Bez tego:
    $ echo ${panstwa[pl]}
    Wielka Brytania

Zostaw komentarz

XHTML: Możesz użyć następujących tagów: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>