Kurzüberblick Linux Shell (Bash, Ash)

Winfried Mueller, www.reintechnisch.de, Start: 10.12.2003

Grundlagen

 
- erste Zeile im Skript gibt den Interpreter an
  #!/bin/ash
  #!/bin/bash

- alles hinter # ist Kommentar

- x-permission setzen, damit es ausgeführt werden kann

- Backticks: Kommando in Subshell und Zuweisung stdout 
  cd /etc
  a=`pwd` # >> /etc

- File include: mittels . Operator
  #!/bin/bash
  . Foo       # Datei Foo wird included

- Variable für ein Subshell übergeben, Environment belassen
  COLUMNS=240 dpkg --list  #COLUMNS ist im Environment
                           #von dpkg, nicht aber in der 
                           #Muttershell

- Boolesche Operationen anders als in C: 
  - true ist 0
  - false ist alles andere, man sollte aber 1 verwenden
  - $ false
    $ echo $?  # >> 1
    $ true
    $ echo $?  # >> 0

- Globbing: 
  - bezeichnet die Auflösung von Platzhalter/Jokerzeichen 
    wie *,? für Dateinamen. 
  - ? : Ein beliebiges Zeichen.
  - * : Beliebige Anzahl eines beliebigen Zeichens.
  - []: Eines der Zeichen, was zwischen den Klammern steht.
        Bsp:
        syslog.[0-9].gz  # >> syslog.1.gz, syslog.2.gz...
        ab[CDEF]         # >> abC, abD, abE, abF
        ab[!0-9]         # >> abA, nicht jedoch ab0, ab1, ab2...
        ab[0-9A-Z]k       # >> ab0k, ab1k, abAk, abZk...
  - {}: Brace Extension: Angegebene Wörter ersetzen. 
        Schachtelung möglich.
        Bsp:
        mail.{info,err}  # >> mail.info, mail.err

- Quoting: 
  - "text $x": Inhalt von Variablen wird ersetzt. Globbing findet
               nicht statt. Sonderzeichen werden intepretiert.
  - 'text $x': Keine Ersetzung, Sonderzeichen wie $, \, ` oder " 
               werden nicht interpretiert.
  - `befehl` : Variablen werden ersetzt, globbing findet statt.
               Befehl wird ausgeführt und dessen stdout 
               zurückgegeben.
  - Quoting fast mehrere Wörter zu einem Wort zusammen:
    Bsp:
    a=Hello World   # >> Fehler
    a="Hello World" # >> OK


- Escape:
  - Folgende Zeichen müssen in Strings geschützt werden:
    \$, \`, \\, \#

- Pipes:
  - verbinden von mehreren Programmen über Pipes, wobei stdout eines
    Programms an stdin des nächsten weitergeleitet wird.
    Bsp:
    $ cat /etc/fstab | grep "hda1"
    $ ls -l | less
  - Exitstatus ist der Status des letzten Befehls in der Pipe.

Ein/Ausgabe-Umleitung

 
  - Eingabeumleitung:
    Bsp:
    $ cat < /etc/fstab # Datei wird an stdin von cat weitergeleitet
  - Ausgabeumleitung
    Bsp:
    $ cat /etc/fstab > /tmp/tmp1.txt  # Umleitung stdout in Datei
    $ cat /etc/fstab >> /tmp/tmp1.txt # Anhängend umleiten
    $ cat /etc/fstab 2> /dev/null     # Fehlerausgaben stderr unterdrücken
    $ cat /etc/fstab 2>&1             # Fehlerausgaben auf stdout umlenken
    $ cat /etc/fstab 1>&2             # Alle Ausgaben nach stderr     

Vordefinierte Variablen

$0Name des Skriptes (ohne Pfad)
$1..$9übergebene Parameter des Skriptes; auch Argumente einer Funktion
$#Anzahl übergebene Parameter
$?Exitstatus des letzten Befehls
$RANDOMZufallszahl Integer
$SHLVLGibt an, wieviele Shells ineinander verschachtelt sind
$OSTYPEBetriebssystem-Typ
$LINENOaktuelle Zeile im Skript, die gerade abgearbeitet wird
$MACHTYPEMaschinentyp
$GROUPSalle Gruppen, in der sich der aktuelle Benutzer befindet (array)

Kontrollstrukturen

 
- bei allen Bedingungen, wird ein Befehl ausgeführt und 
  der Exit-Code dieses Befehls ausgewertet. 0=true, 
  alles andere = false

- weil der Befehl test sehr oft verwendet wird, gibt es
  in der bash und ash eine Abkürzung: 
  test <Bedingung> entspricht [ <Bedingung> ] 
  (Leerzeichen um die eckige Klammern nicht vergessen, weil
   '[' auch nichts weiter als ein Befehl ist, der test aufruft
   und die schließende ']' schluckt.)

- if <befehl> ;then 
    <befehle>; 
  [ elif <befehl> ;then 
    <befehle> ] ...
  [ else 
    <befehle> ] 
  fi
  Bsp:
    if test -d /tmp ;then
      echo "Temp Verzeichnis vorhanden"
    fi

    if [ -d /tmp -a -L /tmp ]
    then
      echo "Temp Verzeichnis vorhanden und ist ein Link"
    fi

- case <ausdruck> in 
    <fall1>) <befehle> ;;
    <fall2>) <befehle> ;;
    ...
  esac
  Bsp:
    a="Test hier"
    case "$a" in
      "Test"*) 
        echo "a fängt mit Test an."
        ;;
      "Foo")
        echo "a ist Foo"
        ;;
      "Bar" | "Sonst")
        echo "a ist Bar oder Sonst"
        ;;
    esac

- while <befehl> ;do
    <befehle>
  done

- until <befehl> ;do
    <befehle>
  done

- for <var> in <wort> ;do
    <befehle>
  done

- break, continue
  - break bricht eine Schleife ab
  - continue startet sofort den nächsten Durchlauf

Shell Verhalten beeinflussen

 
- Benutzung undefinierter Variablen = Fehler
  set -u # einschalten
  set +u # ausschalten
  bash -u <script>

  echo $c # >> bash: c: unbound variable

- Debugging: Jedes Kommando ausgeben bei Abarbeitung
  set -x   # einschalten
  set +x   # ausschalten

- Datei /etc/profile
  - systemweite Startdatei für Login-Shells

- Datei ~/.profile
  - benutzerspezifische Startdatei für Loginshell

- Datei /etc/bash.bashrc
  - systemweite Startdatei für Nich-Login-Shells 
    (nur bash)

- Datei ~/.bashrc
  - benutzerspezifische Startdatei für jede 
    Nicht-Login-Shell (nur bash)

Variablen

 
- erlaubte Zeichen: [A-Za-z_][A-Za-z_0-9]*
- Zuweisung:
  a=10
  str='Hello World'
  str2="Ich bin $a Jahre alt."
- normale Variablen leben solange, wie das Skript läuft
- Benutzung:
  - $ voranstellen, um den Wert auszulesen, oder ${var_name}
    a=10
    echo $a # >> 10
    echo a  # >> a
    b=a     # >> a
    b=$a    # >> 10
    echo "Das Skript laeuft ${n}mal"  # ${n} anstatt $n 

  - Listenexpansion/Globbing wird wird unterdrückt,
    wenn in doppelte Anführungsstriche
    a='/etc/*'
    echo $a   # >> alle Files unter etc
    echo "$a" # >> /etc/*

  - $ Entwertung duch \
    echo \$a  # >> $a

- lokale Variablen in Funktionen
  - sind nach außen nicht sichtbar, verändern nicht
    außen definierte Variablen. 
  - local a
  - local b=10

- Variablen mit export zum Environment hinzufügen
  - Subshell erben das Environment, so auch dort 
    verfügbar; erben = eigene Kopie des Environments
  - nicht exportiere Variablen sind für Subshells 
    unsichtbar
  - 
  - a=10
    export a
    export a=10 # Zuweisung + export geht in bash

- Arrays
  - anlegen:
  a[10]=10
  a[9]="Hello"
  - auslesen:
  echo ${a[10]}
  echo ${a[9]}
  echo ${a[*]} # alle Werte durch Space getrennt ausgeben
  echo ${a[@]} # identisch in diesem Fall
  - Mehrfachzuweisung:
  a=(1 10 Hello Test) # >> ${a[0]}=1; ${a[1]}=10; ${a[2]="Hello"}...
  - Länge eines Eintrages
  echo ${#a[2]}  # >> 5 bei obigen Beispiel "Hello"
  - Anzal der Einträge
  echo ${#a[*]}  # >> 4 in obigen Beispiel

Wichtige Befehle

 
export var
  - ohne Parameter oder mit -p Anzeige des Environments
  - export von Variablen, siehe unter Variablen

unset var
  - Environmentvariable löschen
    unset Foo

shift [n]
  - verschiebt alle Übergabeparameter nach links, $1 fällt 
    raus, $2 wird zu $1 usw. Mit n kann man angeben, um wieviel
    Positionen geshiftet wird. Default = 1

exit [n]
  - Skript verlassen/beenden. Wert 0 steht für normale Beendung. 
    Fehler können mit Fehlerwerten 1-255 zurückgegeben werden. 

true und false
  - true gibt 0 zurück, false gibt 1 zurück

env oder printenv
  - listet das Environment auf, ähnlich export ohne Parameter

expr
  - Berechnungen durchführen, Integer Arithmetik
  - String Regexp-Vergleiche
  - substr-Vergleiche
  - String-Länge
  - Leerzeichen zwischen Wert und Operator nicht vergessen 
  - quoting einiger Sonderzeichen (Operatoren) nicht vergessen 
  - Bsp:
    $ echo `expr 3 + 2`              # >> 5
    $ x=1; x=`expr $x + 1`; echo $x  # >> 2 
    $ echo `expr 3 + 2 \* 5 - 1 / 2`

tail

head

tee
  - stdout wird sowohl nach stdout wie auch in eine Datei 
    verzweigt. So kann man sich Programmausgaben sowohl auf
    der Konsole anschauen wie auch zusätzlich in eine Datei 
    schreiben.
    Bsp:
    $ grep "err" /var/log/syslog | tee /tmp/errlist.txt

test
  - wird häufig verwendet zum Testen aller möglichen Bedingungen.
    Ausdruck        Beispiel         Erklärung
    -d file         [ -d /etc ]      Wahr, wenn file ein Directory
    -f file         [ -f fstab ]     Wahr, wenn File existiert
    -r file         [ -r fstab ]     Wie -f und Leseberechtigung
    -w file         [  -w fstab ]    Wie -f und Schreibberechtigung
    -x file         [ -x shred ]     Wie -f und Ausführberechtigung
    -L file         [ -L /tmp  ]     File ist ein symbolischer Link
    -h file                          identisch mit -L
    -g file         [ -g /tmp  ]     Wahr, wenn gid Bit gesetzt
    -k file                          Wahr, wenn Sticky Bit gesetzt
    -u file                          Wahr, wenn suid Bit gesetzt
    file1 -nt file2                  Wahr, wenn file1 neuer als file2
    file1 -ot file2                  Wahr, wenn file1 älter als file2

    -z string       [ -z $str ]      Wahr, wenn String Länge 0 hat.
    -n string       [ -n $str ]      Wahr, wenn String nicht leer ist.
    str1 = str2     [ "a" = "a" ]    Wahr, wenn Strings identisch.
    str1 != str2    [ "a" != "A" ]   Wahr, wenn Strings ungleich.

    z1 -eq z2       [ 10 -eq 10 ]    Wahr, wenn z1 gleich z2
    z1 -lt z2       [ 10 -lt 20 ]    Wahr, wenn z1 < z2
    z1 -le z2       [ 10 -le 20 ]    Wahr, wenn z1 <= z2
    z1 -ne z2       [ 10 -ne 20 ]    Wahr, wenn z1 <> z2
    z1 -gt z2       [ 10 -gt 20 ]    Wahr, wenn z1 > z2
    z1 -ge z2       [ 10 -ge 20 ]    Wahr, wenn z1 >= z2

    ! ausdruck      [ ! 1 -ge 2 ]    Kehrt Ausdruck um
    ausd1 -a ausd2 [ -z $a -a -z $b ] Und Verknüpfung 
    ausd1 -o ausd2 [ -z $a -o -z $b ] Oder Verknüpfung

type Name
  zeigt an, wie Name interpretiert werden würde
  -t : gibt alias, keyword, function, builtin, file zurück, je
       nachdem, wie der Name interpretiert würde.
  -a : Ausgabe aller Speicherorte, wo dieses ausführbare File 
       gefunden werden kann. 

file file
  - Type/Format des Files zurückgeben
  - lässt sich erweitern über /etc/magic
  - file ist nicht gut portabel

echo text
  - Ausgabe Text auf stdout
  -n verhindert Ausgabe von newline

printf format arg...
  - Formatierte Ausgabe

read var
  - Eingaben von stdin entgegennehmen und in var ablegen
  - von anderen Kanälen lesen:
    read pass <&4

env [NAME=VALUE]...[COMMAND [ARG]...] 
  - Ein Programm mit verändertem Environment aufrufen.
  - ohne Parameter listet es das Environment auf

tr [OPTION]...SET1 [SET2]
  - Filterwerkzeug, was auf stdin empfängt und auf stdout ausgibt
  - löschen einzelner Zeichen
    echo "Hello World" | tr -d "or"   # >> Hell Wld
  - löschen von Wiederholungen eines Zeichens
    echo "Hello Worldddd" | tr -s "ld" # >> Helo World
  - Ersetze alle Zeichen in SET1 durch SET2 
    echo "ABCDEFGHI" | tr "ABC" "abc" # >> abcDEFGHI 
  - Ersetze alle Zeichen, die nicht in SET1 stehen durch SET2
    echo "ABCDEFGHI" | tr -c "ABC\n" "X"  # >> ABCXXXXX

Wichtige Environment-Variablen

PATHSuchpfad, durch Doppelpunkt getrennt
HOMEHomeverzeichnis des aktuellen Benutzers
PS1Festlegung des Eingabe-Prompts
PWDaktuelles Verzeichnis
OLDPWDvorheriges Verzeichnis
SHELLderzeit ausgeführte Shell
USERBenutzername des aktuellen Benutzers
LOGNAMEName es eingeloggten Benutzers
MAILPosteingangs-mbox
TERMverwendetes Terminal

Funktionen

 
- Definition
  - function MyFunction ()  # bash typisch, ash nicht
    {
      # Code
    }
  - MyFunction ()           # ash, geht aber auch bei bash
    {
      # Code
    }
  - Variablenübergabe mit $1, $2 usw.
    function Foo ()
    {
      local src_file=$1
      local dst_file=$2
      # ...
    }


Here Documents

 
- eine spezielle komfortable Art, längere Texte im Quelltext 
zu verfassen. Dieser Text wird auf stdin eines Kommandos geleitet. 
Text wird eingefasst von <<MARKE bis MARKE. MARKE
kann dabei ein beliebiges Wort sein.

- Bsp:
  s1="EinBeispielSkript"
  s2="0.0.1"
  cat <<ENDE
Das ist ein langer Text,
der sich über mehrere Zeilen,
erstreckt. Genau so, wie ich den
hier eintippe, so erscheint er später
bei der Ausgabe. Das Skript heißt $s1
und hat die Release: $s2
ENDE

- Anfangs- und Endemarke müssen identisch sein, keine Leerzeichen dahinter
hängen. Endemarke muss einzeln auf Zeile stehen.

- Ideal, um Texttemplates mit Variablenwerten zu ersetzen, z.B. für dynamische
Erzeugung von Websites oder für Serienbriefe. 

Tricks

 
Umleitung eines Blocks
  - Die Ausgaben in einem Block können gesammelt umgeleitet werden
    Bsp:
    if true
      echo "Hello"
      echo "Hello"
    fi >> test.txt

Pitfalls

 
- Variablenzuweisung keine Leerzeichen
  a=10    # ok
  a = 10  # err
  a= 10   # err

- Bei expr immer Leerzeichen verwenden
  expr 20 / 10 #ok
  expr 20/10   #err

- Variablen, die einen Dateinamen beinhalten, sollten immer in "
  geschrieben werden, weil sie Leerzeichen enthalten können.
  Bsp:
  ls "$f"

Tastenkombinationen

  • strg+s - XOFF
  • strg+q - XON
  • strg+d - Abschluss diverser Programme, z.B. mail