Shell Skripte - What to know about
Vorbemerkung
Shell-Scripts (Kommandoprozeduren) sind unter Unix das Analogon zu Batch-Dateien (Stapeldateien) von MS-DOS, sie sind jedoch wesentlich leistungsfähiger. Ihre Syntax hängt allerdings von der verwendeten "Shell" ab. In diesem Artikel werden Bourne Shell (sh) Scripts betrachtet. Sie gelten im allgemeinen als zuverlässiger als csh-Scripts.
Allgemeines zu Shell Scripts
Ein Shell-Script ist eine Textdatei, in der Kommandos gespeichert sind. Es stellt selbst ein Kommando dar und kann wie ein Systemkommando auch mit Parametern aufgerufen werden. Shell-Scripts dienen der Arbeitserleichterung und (nach ausreichenden Tests) der Erhöhung der Zuverlässigkeit, da sie gestatten, häufig gebrauchte Sequenzen von Kommandos zusammenzufassen.
Aufruf
Bourne Shell Scripts lassen sich generell wie folgt aufrufen:
bash myscript.sh
Im allgemeinen ist es aber praktischer, die Datei als ausführbar anzumelden (execute permission), mittels
chmod +x myscript.sh
Das Shell-Script läßt sich dann wie ein "normales" (binäres) Kommando aufrufen:
./myscript.sh
Vorausgesetzt ist hierbei allerdings, daß das Betriebssystem das Script mit der richtigen Shell abarbeitet. Moderne Unix-Systeme prüfen zu diesem Zweck die erste Zeile. Für die sh sollte sie folgenden Inhalt haben:
#/bin/sh
oder für die BASH
:
#!/bin/bash
oder nach neuem Standard:
#!/usr/bin/env bash
Obwohl das Doppelkreuz normalerweise einen Kommentar einleitet, der vom System nicht weiter beachtet wird, erkennt es hier, daß die "sh" (mit absoluter Pfadangabe: /bin/bash) eingesetzt werden soll. Für weitere Informationen:
Einfaches Beispiel
#!/bin/sh # Einfaches Beispiel echo Hallo, Welt! echo Datum, Uhrzeit und Arbeitsverzeichnis: date pwd echo Uebergabe-Parameter: $*
Das vorstehende einfache Script enthält im wesentlichen normale Unix-Kommandos. Abgesehen von der ersten Zeile liegt die einzige Besonderheit im Platzhalter "$*", der für alle Kommandozeilen-Parameter steht.
Skript Header Vorlage
#!/usr/bin/env bash
#########################################################################
#Name: myscript.sh
#Subscription: This script makes things easier :-)
#by A. Laub
#andreas[-at-]laub-home.de
#
#License:
#This program is free software: you can redistribute it and/or modify it
#under the terms of the GNU General Public License as published by the
#Free Software Foundation, either version 3 of the License, or (at your option)
#any later version.
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
#or FITNESS FOR A PARTICULAR PURPOSE.
#########################################################################
#Set the language
export LANG="en_US.UTF-8"
#Load the Pathes
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
#set the variables:
#do the stuff:
Testen eines Shell-Scripts
Syntax-Test (die Kommandos werden gelesen und geprüft, aber nicht ausgeführt)
sh -n myscript.sh
Ausgabe der Shell-Kommandos in der gelesenen Form
sh -v myscript.sh
Ausgabe der Shell-Kommandos nach Durchführung aller Ersetzungen, also in der Form, wie sie ausgeführt werden
sh -x myscript.sh
Testen eines Bash-Scripts
Syntax-Test (die Kommandos werden gelesen und geprüft, aber nicht ausgeführt)
bash -n myscript.sh
Ausgabe der Shell-Kommandos in der gelesenen Form
bash -v myscript.sh
Ausgabe der Shell-Kommandos nach Durchführung aller Ersetzungen, also in der Form, wie sie ausgeführt werden
bash -x myscript.sh
Kommandozeilen-Parameter
$0
Name der Kommandoprozedur, die gerade ausgeführt wird
$#
Anzahl der Parameter $1 $2 $3 ... erster, zweiter, dritter ... Parameter
$*
steht für alle Kommandozeilen-Parameter ($1 $2 $3 ...)
$@
wie $* ($1 $2 $3 ...) "$@" expandiert (im Unterschied zu "$*") zu: "$1" "$2" "$3" ...
$$
Prozeßnummer der Shell (nützlich, um eindeutige Namen für temporäre Dateien zu vergeben)
$-
steht für die aktuellen Shell-Optionen
$?
gibt den Return-Code des zuletzt ausgeführten Kommandos an (0 bei erfolgreicher Ausführung)
$!
Prozeßnummer des zuletzt ausgeführten Hintergrund-Prozesses
Beispiel:
#!/bin/sh # Variablen echo Uebergabeparameter: $* echo user ist: $USER echo shell ist: $SHELL echo Parameter 1 ist: $1 echo Prozedurname ist: $0 echo Prozessnummer ist: $$ echo Anzahl der Parameter ist: $# a=17.89 # ohne Luecken am = Zeichen echo a ist $a
Prozeßsteuerung
Bedingte Ausführung: if
if [ bedingung ] then kommandos1 else kommandos2 fi
Anmerkungen: "fi" ist ein rückwärts geschriebenes "if", es bedeutet "end if" (diese Schreibweise ist eine besondere Eigenheit der Bourne Shell). Die "bedingung" entspricht der Syntax von test, siehe auch weiter unten. Im if-Konstrukt kann der "else"-Zweig entfallen, andererseits ist eine Erweiterung durch einen oder mehrere "else if"-Zweige möglich, die hier "elif" heißen:
if [ bedingung1 ] then kommandos1 elif [ bedingung2 ] then kommandos2 else kommandos3 fi
Die Formulierung
if [ bedingung ]
ist äquivalent zu
if test bedingung
Alternativ ist es möglich, den Erfolg eines Kommandos zu prüfen:
if kommando
(beispielsweise liefert das Kommando "true" stets "wahr", das Kommando "false" hingegen "unwahr")
Wichtige Vergleichsoperationen (test) Hinweis: Es ist unbedingt notwendig, daß alle Operatoren von Leerzeichen umgeben sind, sonst werden sie von der Shell nicht erkannt! (Das gilt auch für die Klammern.) Zeichenketten
"s1" = "s2"
wahr, wenn die Zeichenketten gleich sind
"s1" != "s2"
wahr, wenn die Zeichenketten ungleich sind
-z "s1"
wahr, wenn die Zeichenkette leer ist (Länge gleich Null)
-n "s1"
wahr, wenn die Zeichenkette nicht leer ist (Länge größer als Null)
(Ganze) Zahlen
n1 -eq n2
wahr, wenn die Zahlen gleich sind
n1 -ne n2
wahr, wenn die Zahlen ungleich sind
n1 -gt n2
wahr, wenn die Zahl n1 größer ist als n2
n1 -ge n2
wahr, wenn die Zahl n1 größer oder gleich n2 ist
n1 -lt n2
wahr, wenn die Zahl n1 kleiner ist als n2
n1 -le n2
wahr, wenn die Zahl n1 kleiner oder gleich n2 ist
Sonstiges
!
Negation
-a
logisches "und"
-o
logisches "oder" (nichtexklusiv; -a hat eine höhere Priorität)
\( ... \)
Runde Klammern dienen zur Gruppierung. Man beachte, daß sie durch einen vorangestellten Backslash, \, geschützt werden müssen.
-f filename
wahr, wenn die Datei existiert. (Weitere Optionen findet man in der man page zu test)
! -f filename
wahr, wenn die Datei nicht existiert.
-d directory
wahr, wenn der Ordner existiert. (Weitere Optionen findet man in der man page zu test)
! -d directory
wahr wenn der Ordner nicht existiert
Beispiel:
#!/bin/sh # Interaktive Eingabe, if-Abfrage echo Hallo, user, alles in Ordnung? echo Ihre Antwort, n/j: read answer echo Ihre Antwort war: $answer # if [ "$answer" = "j" ] if [ "$answer" != "n" ] then echo ja else echo nein fi
Mehrfachentscheidung: case
case var in muster1) kommandos1 ;; muster2) kommandos2 ;; *) default-kommandos ;; esac
Anmerkungen: "esac" ist ein rückwärts geschriebenes "case", es bedeutet "end case". Die einzelnen Fälle werden durch die Angabe eines Musters festgelegt, "muster)", und durch ein doppeltes Semikolon abgeschlossen. (Ein einfaches Semikolon dient als Trennzeichen für Kommandos, die auf derselben Zeile stehen.) Das Muster "*)" wirkt als "default", es deckt alle verbleibenden Fälle ab; die Verwendung des default-Zweiges ist optional.
Beispiel:
#!/bin/sh # Interaktive Eingabe, Mehrfachentscheidung (case) echo Alles in Ordnung? echo Ihre Antwort: read answer echo Ihre Antwort war: $answer case $answer in j*|J*|y*|Y*) echo jawohl ;; n*|N*) echo nein, ueberhaupt nicht! ;; *) echo das war wohl nichts ;; esac
Schleife: for
for i in par1 par2 par3 ... do kommandos done
Anmerkungen: Die for-Schleife in der Bourne Shell unterscheidet sich von der for-Schleife in üblichen Programmiersprachen dadurch, daß nicht automatisch eine Laufzahl erzeugt wird. Der Schleifenvariablen werden sukzessive die Parameter zugewiesen, die hinter "in" stehen. (Die Angabe "for i in $*" kann durch "for i" abgekürzt werden.)
Beispiel:
#!/bin/sh # Schleifen: for echo Uebergabeparameter: $* # for i for i in $* do echo Hier steht: $i done
Zahlen Bereiche in for Schleife
Dieses Beispiel zeigt eine Schleife, die bis 5 hoch zählt
#!/bin/bash
for i in 1 2 3 4 5
do
echo "Welcome $i times"
done
oder einfacher, vor allem wenn man mehr als 5 Zahlen braucht
#!/bin/bash
for i in {1..5}
do
echo "Welcome $i times"
done
Hier wird bis 10 in 2er Schrittenhochgezählt
#!/bin/bash
echo "Bash version ${BASH_VERSION}..."
for i in {0..10..2}
do
echo "Welcome $i times"
done
Schleife: while und until
while [ bedingung ] do kommandos done until [ bedingung ] do kommandos done
Anmerkung: Bei "while" erfolgt die Prüfung der Bedingung vor der Abarbeitung der Schleife, bei "until" erst danach. (Anstelle von "[ bedingung ]" oder "test bedingung" kann allgemein ein Kommando stehen, dessen Return-Code geprüft wird; vgl. die Ausführungen zu "if".)
Beispiel:
#!/bin/sh # Schleifen: while # mit Erzeugung einer Laufzahl i=1 while [ $i -le 5 ] do echo $i i=`expr $i + 1` done