Shell Skripte - What to know about

Aus Laub-Home Wiki

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

Quellen