Aufzucht und Hege von Shell-Scripts: Variablen

Gültigkeit von Variablen

Eine in der Shell mit VAR=wert definierte Variable ist in dieser Shell (und nur in dieser) bekannt. Wenn Ihr in eine Subshell abtaucht, dann kennt man sie dort nicht mehr. Um eine Variable in den Subshells bekannt zu machen, müsst Ihr sie exportieren:

  jan@jack:~/tmp> VAR=1
  jan@jack:~/tmp> sh -c "echo $VAR"  # $VAR wird in der aktuellen Shell expandiert
  1
  jan@jack:~/tmp> sh -c 'echo $VAR'  # $VAR wird in der Subshell expandiert

  jan@jack:~/tmp> export VAR         # Export
  jan@jack:~/tmp> sh -c 'echo $VAR'  # jetzt ist $VAR in der Subshell bekannt
  1
  jan@jack:~/tmp> VAR=2
  jan@jack:~/tmp> sh -c 'echo $VAR'  # eine Wertänderung erfordert keinen neuen export
  2

Was nie funktioniert: Variablenänderungen in einer Subshell an die aufrufende Shell hochzureichen. Das wird besonders dann gern vergessen, wenn man z. B. eine while-Schleife über eine Pipe füttert - eine Pipe macht eine Subshell auf (siehe auch Sub-Shells):

  jan@jack:~/tmp> sh -c 'VAR=3'
  jan@jack:~/tmp> echo $VAR
  2
  jan@jack:~/tmp> ls | while read x; do VAR=$x; done
  jan@jack:~/tmp> echo $VAR
  2

Es gibt zwei Möglichkeiten, die ermittelten Werte nach oben durchzureichen: den Wert auf STDOUT auszugeben und in der aufrufenden Shell per Kommandosubstitution "einzufangen", oder auf Subshells zu verzichten (zu diesem Thema siehe die nächsten Abschnitte):

  jan@jack:~/tmp> VAR=`sh -c 'VAR=$((VAR + 1)); echo $VAR'`
  jan@jack:~/tmp> echo $VAR
  3

In der "globalen" Gültigkeit von Shell-Variablen liegt eine Falle, besonders für rekursiv arbeitende Funktionen. Dazu ein kleines, ziemlich sinnfreies Beispiel:

  function rekursiv {
    # ein Zaehler
    zahl=0
    # Rekursionstiefe, wird mir uebergeben, ich zaehle 1 hoch
    tiefe=`expr $1 + 1`
    # Schleife, solange $zahl kleiner 3 und Rekursionstiefe kleiner 3
    while test $zahl -le 2 -a $tiefe -le 2
    do
      # ich gebe meine Werte aus
      echo "zahl=$zahl tiefe=$tiefe"
      # ich steige eine Rekursionsebene hinab
      rekursiv $tiefe
      # Falle! Hier ist $tiefe bereits 2, da sie global gueltig ist
      # und in den Rekursionen hochgezaehlt wurde
      zahl=`expr $zahl + 1`
      # hier Schleifenausstig, weil Ende-Bedingung erreicht ist. Durchlauf mit $zahl = 1
      # findet nie statt
    done
  }

Dem Problem kann man zumindest in der Bash begegnen, wenn man die Variablen lokal definiert:

  function rekursiv {
    # ein Zaehler
    local zahl=0
    # Rekursionstiefe, wird mir uebergeben, ich zaehle 1 hoch
    local tiefe=`expr $1 + 1`
    ...
  }