shellscripts:shellscripts

Dies ist eine alte Version des Dokuments!


Shellscripts

Eine Sammlung eigener und fremder Shellskripte.

Grundsätzlich macht man ein Shellsripte ausführbar (chmod +x DATEI).

Für den Debugmodus ändert man einfach die erste Zeile, der sog. Shebang, in

  #!/bin/sh -x
  • dauerhaft
    • Systemweit:
      echo "set bell-style none" >> /etc/inputrc
    • für den aktuellen Benutzer:
      echo "set bell-style none" >> ~/.inputrc
  • sofort
    • Console:
      • Disable: setterm -blength 0
      • Enable: setterm -blength 200
    • X11:
      • Disable: xset b off
      • Enable: xset b on
Umleitungsschema Wirkung
Befehl > ausgabe.log

(auch:

Befehl 1> ausgabe.log
Die (Standard-)Ausgabe (STDOUT) wird in die Datei ausgabe.log geschrieben (diese Datei wird, falls vorhanden, komplett durch die Ausgabe von cmd überschrieben.
Befehl > ausgabe.log
Die (Standard-)Ausgabe (STDOUT) wird an die Datei ausgabe.log angehängt
Befehl 2> ausgabe.log
Die (Standard-)Fehlerausgabe (STDERR) wird in die Datei ausgabe.log geschrieben
Befehl 2>> ausgabe.log
Die (Standard-)Fehlerausgabe (STDERR) wird an die Datei ausgabe.log angehangen
BefehlA | BefehlB 
Ausgabe des BefehlA an BefehlB übergeben (pipen)
Befehl < eingabe.txt
Die (Standard-)Eingabe wird aus der Datei eingabe.log genommen (nicht von der Tastatur).
Befehl << EOT eingabe.log
Die (Standard-)Eingabe wird aus der Datei eingabe.log genommen (nicht von der Tastatur). Aber nur bis zur (frei wählbaren!) Zeichenfolge EOT. Es handelt sich um ein HERE-Dokument.
Numeric handles:
Name Bedeutung
STDIN = 0 Keyboard input
STDOUT = 1 Text output
STDERR = 2 Error text output
UNDEFINED = 3-9
Umleitung nach /dev/null (Fehlermeldungen verbergen):
Befehl 2> /dev/null 
Fehlermeldungen an /dev/null umleiten
Befehl >/dev/null 2>&1 
Fehlermeldungen und Ausgaben an /dev/null umleiten
Befehl >Dateiname 2> /dev/null 
Fehlermeldungen an /dev/null, die Ausgaben jedoch an „Dateiname“ umleiten
  • STDERR und STDOUT an ausgabe.log anhängen:
    die echo ausgabe.log >> test.txt 2>&1
  • die Eingabe aus eingabe.txt holen, STDOUT auf ausgabe.log und STDERR auf fehler.log leiten
    cmd < eingabe.txt > ausgabe.log 2> fehler.log
  • die Eingabe aus eingabe.log holen und STDERR auf STDOUT (2>&1) umlenken:
    cmd < eingabe.txt > ausgabe.log 2>&1

    wobei das ganze auch einfacher als

    cmd < eingabe.txt &> ausgabe.log

    geschrieben werden kann.

  • Fehlermeldungen und Ausgaben an „Dateiname“ anhängen:
    Befehl > Dateiname 2>&1 
  • Ausgaben und Fehlermeldungen an „Dateiname“ anhängen:
    Befehl > Dateiname 2<&1 

Den Begriff Returncode bzw. Exitlevel vorher: Rückgabe von „0“ an das Betriebssystem heißt Erfolg, alles andere (Returncode ≠ 0) sind Fehler.

Schema Wirkung
cmd1 | cmd2 Pipe: Die Standardausgabe des ersten Befehls (cmd1) ist die Eingabe des zweiten Befehls.
cmd1; cmd 2 Einfache Verküpfung: cmd2 wird gestartet wenn cmd1 beendet ist (egal ob erfolgreich oder nicht).
cmd1 & cmd2 cmd1 wird im Hintergrund ausgeführt, während dessen wir schon cmd2 ausgeführt
cmd1 && cmd2 cmd2 wird nur ausgeführt wenn cmd1 fehlerfrei beendet wurde (Returncode = 0)
cmd1 || cmd2 cmd2 wird nur ausgeführt wenn cmd1 fehlerhaft beendet wurde (Returncode ≠ 0)

Der Befehl test (oft nur über die Aliase der eckigen Klammern [ ] benutzt) prüft verschiedene Bedingungen ab und liefert einen Boolean-Rückgabewert (true oder false) zurück.

Beispiel:

if [ zeichenkette1 == zeichenkette2 ]
then
   echo "Zeichenketten sind gleich."
else
   echo "Zeichenketten sind ungleich."
fi

Auch reguläre Ausdrücke sind möglich:

[[ "string" =~ pattern ]]
Tests für Dateien
Syntax (name = Dateiname) testet was?
-d name name ist ein Verzeichnis
if [ ! "`ls -A $verz`" ]
then
        echo "leer!"
fi
Verzeichnis leer?
-e name name ist eine Datei
-f name name ist eine reguläre Datei
-L name name ist ein symbolischer Link
-r name name existiert und ist lesbar
-w name name existiert und ist schreibbar
-x name name existiert und ist ausführbar
-s name name existiert und hat eine von Null unterschiedliche Größe
name1 -nt name2 name1 ist jünger als name2
name1 -ot name2 name1 ist älter als name2
Tests für Zeichenketten (Strings)
Syntax testet was?
s1 == s2 s1 und s2 sind gleich
s1 != s2 s1 und s2 sind ungleich
-z s1 s1 hat Länge Null
-n s1 s1 hat Länge ungleich Null
nummerische Tests, für ganzzahlige (Integer)
Syntax testet was?
a -eq b a und b sind gleich
a -ne b a und b sind ungleich
a -gt b a größer als b
a -ge b a größer-gleich als b
a -lt b a kleiner als b
a -le b a kleiner-gleich b
Kombinationen und Negationen (Umkehrungen)
SyntaxWirkung
test1 -a test2 test1 und test2 sind wahr
test1 -o test2 test1 oder test2 sind wahr
! Bedingung Testnegationen: Bedingung ist falsch
\( Bedingung \) Verkettung bzw. Gruppieren von Bedingungen: Backslash \ schützt die Klammern

Die Ausgabe von Programmen in einer Variable speichern:

Datum=`date`

oder besser (weil verschachtelbar!):

Datum=$(date)
if [ -z $1 ]; then
 echo "empty parameter!"
 echo "usage: $0 [Parameter]"
 exit
fi
Code

oder leicht einfacher (und umgedreht):

if [ $1 ]; then
 CODE
else
 echo "empty parameter!"
 echo "Usage: $0 [Parameter]"
fi
if [ $? -ne 0 ]
        then
                echo Fehler aufgetreten
                exit 1
        else
                Code
fi
if [ "`id -u`" -eq 0 ]; then
 echo you are root!
else
 echo you are not root!
fi

Posix-kompatibel:

echo -n "Is this a good question (y/n)? "
read answer
if [ "$answer" != "${answer#[Yy]}" ] ;then
    echo Yes
else
    echo No
fi

Bash:

echo "Do you wish to install this program?"
select yn in "Yes" "No"; do
    case $yn in
        Yes ) make install; break;;
        No ) exit;;
    esac
done

How do I prompt for Yes/No/Cancel input in a Linux shell script?

Einfache Dateien können mit „echo Inhalt > Datei“ erzeugt werden, was aber wenn komplexe Konfigurationsdateien erzeugt werden müssen?

cat > "Dateiname" << EOF
  <Directory />
        AllowOverride AuthConfig FileInfo Limit Indexes
        Order allow,deny
        allow from all
  </Directory>
EOF

von 1.. bis 10 zählen:

#!/bin/bash
i=1
while [ $i -le 10 ]
do
  echo $i
  let i=$i+1 #nur bash
  # i=`expr $i + 1` # universal
done
for zeile in $(cat listendatei.txt); do
 echo $zeile
done

bzw. diese Alternativlösung wenn die Zeilen auch Leerzeichen enthalten könnten:

while read -r zeile ; do
   echo "$zeile"
done < listendatei.txt

sed:

  • vorne:
    sed 's/^[[:blank:]]*//'
  • hinten:
    sed 's/[[:blank:]]*$//'
  • vorne+hinten:
    sed 's/^[[:blank:]]*//;s/[[:blank:]]*$//'

tr (mehr als ein Leerzeichen):

  • tr -s ' '

bash-only-Lösung:

  • vorne:
    shopt -s extglob
    echo ${text##+([[:space:]])}
  • hinten:
    shopt -s extglob
    echo ${text%%+([[:space:]])}
  • alle Leerzeichen entfernen:
    echo ${text//[[:space:]]}
#!/bin/bash
 
set -e -o pipefail
 
function cleanup {
  EXIT_CODE=$?
  set +e # disable termination on error
  rm "$tempfile"
  exit $EXIT_CODE
}
trap cleanup EXIT

Quelle

Aus einem Pfad bekommt man mit basename den Dateinamen heraus. Andere Möglichkeiten

  • nur den Dateinamen:
    echo "$DATEINAME" | cut -d. -f1
  • nur die Erweiterung:
    echo $DATEINAME | awk -F . '{print $NF}'
  • pro Aufruf eine Ebene wegnehmen (bei mehreren Punkten im Dateinamen):
    • 1 Ebene:
      echo "$DATEINAME" | sed s/\.[^\.]*$//
    • 2 Ebenen (usw.):
      echo "$DATEINAME" | sed s/\.[^\.]*$// | sed s/\.[^\.]*$//

Anwendungsbeispiel: Vor der Erweiterung etwas einfügen.

a="Dateiname.ext"
xclude_ext=`echo "$DATEINAME" | cut -d. -f1`
ext=`echo $DATEINAME | awk -F . '{print $NF}'`
mv "$a" "$xclude_ext"-Zwischentext."$ext"

In einigen Fällen ist es nötig den Benutzer eine Auswahl treffen zu lassen. Dazu bieten sich einige Programme an, zenity ist sicherlich eine der besseren Programmen; es basiert auf dem GTK+-Toolkit und zeigt die Texte lokalisiert an (siehe Screenshot).

Anzeigebeispiel (Siehe auch: A complete zenity dialog examples #1 und #2 und das Zenity-Handbuch z.B. zum Thema Fortschrittsanzeige-Dialog):

Zenity Screenshot

Die Auswertung der Antwort(en) ist relativ simpel:

  • ausgewählte Optionen werden komplette als Text zurückgegeben, wenn Mehrfachauswahlen zugelassen sind, dann sind diese durch ein „|“-Symbol getrennt. Um in diesem Fall die Antworten besser auswerten zu können, könnte man durch anfügen von --separator=„\n“ als Trenner einen Zeilenumbruch nehmen und dann eine for-Schleife über alle Antworten laufen lassen:
    for dev in $answer
    do
    	echo "answer is $dev"
    done
  • OK ist Errorlevel 0 (Variable $? prüfen)
  • Cancel ist Errorlevel 1 (Variable $? prüfen)
answer=`zenity --title="main menu" --width=300 --height=200 --list  --radiolist  --column "select" --column "choice" TRUE choice1 FALSE choice2 FALSE "choice3 with more text"`
if [ $? -eq 1 ]; then
	echo "cancel pressed"
	exit
else
	echo "OK pressed"
	case "$answer" in
	choice1)
 
		echo "choice1"
		;;
	choice2)
		echo "choice2"
		;;
	choice3)
		echo "choice3"
		;;
	*)
		echo "case $answer not implemented yet."
		;;
	esac
fi

konkrete Programme

  • Checkmd5: md5-Summen von vielen Dateien mit einzelnen md5-Prüfsummendateien
  • Rename (Wahrscheinlich das meist-vermisste bei Umsteigern)
  • Rename2 noch ein Rename, sollte ich vielleicht in Par2 check+unpack einbauen um die vol-Dateien automatisch umzubenennen!

alt.bin.repair - umfangreiches Shellscript zum Prüfen und entpacken von .par2-sets aus dem usenet.

  • encode: zum enkodieren von Videos (Aufruf ./encode DATEI)
  • vidcut: zum Extrahieren einer Sequenz aus einem Video
  • vmerge: um mehrere Videos zusammenzufügen

CLI Magic: Video conversion with mencoder