Alles, was man auf der Kommandozeile eingeben kann, kann man auch in einem Skript verwenden. Umgekehrt gilt das gleiche

Foliensatz 7 Shell-Programmierung Inhalt • Aufbau Shell-Skript • Tests • Eingabe- und Ausgabebefehle • Verzweigungen • Schleifen • Funkti...
Author: Ralf Krämer
0 downloads 2 Views 150KB Size
Foliensatz 7

Shell-Programmierung

Inhalt •

Aufbau Shell-Skript



Tests



Eingabe- und Ausgabebefehle



Verzweigungen



Schleifen



Funktionen



Hilfreiche (Bash-)Befehle

Shell-Skript •

Ein Shell-Skript ist einen Textdatei, die von der Shell interpretiert wird. Um ein möglichst portables Shell-Skript zu schreiben, darf man nur die POSIX-konforme Syntax und die POSIX-konformen Befehle verwenden. Da aber die Bash die Standardshell der meisten Linux-Distributionen und /bin/sh zumeist ein Link auf /bin/bash ist, ist es in den meisten Fällen okay, auch spezielle Bash-Befehle zu verwenden.



Alles, was man auf der Kommandozeile eingeben kann, kann man auch in einem Skript verwenden. Umgekehrt gilt das gleiche. Daher ist es möglich, zuerst direkt auf der Kommandozeile Befehle zu kombinieren und auszuprobieren und später daraus ein Skript zu machen.

Eine gute Dokumentation, die kurz und bündig ist, aber viele Beispiele umfasst, ist der Advanced BashScripting Guide.

Shell-Skript - Aufbau •



In einem Shell-Skript werden meist überwiegend Shell-Befehle benutzt. Zu den Shell-Befehlen gehören unter anderem: •

Variablenzuweisungen



Tests (test, [ ... ], [[ ... ]])



Verzweigungen (if, case)



Schleifen (for, while, until)



Ein- und Ausgabebefehle (read, echo, printf)



Funktionen (function)

Darüber hinaus können natürlich auch alle anderen Programme wie head, tail, cut, … verwendet werden.

Technische Praxis der Computersysteme 1 | Wintersemester 2013 | Version: 2013-12-07 10:16

Seite 1 von 10



Um ein Skript ausführbar zu machen, muss das Executable-Bit auf die Datei gesetzt werden und in der ersten Zeile muss ein entsprechender Shebang stehen (für portable Skripte #!/bin/sh, sonst #!/bin/bash). Oder man ruft das Skript direkt mit Hilfe der Bash auf, also bash skript.sh.

Shell-Skript - Stellungsparameter •

Einem Shell-Skript können beim Aufruf wie jedem anderen Befehl Argumente übergeben werden. Die Shell stellt für das Arbeiten mit den Argumenten die Stellungsparameter sowie einige spezielle Parameter zur Verfügung.



$1, $2, $3, … sind die Stellungsparameter. In $1 ist das erste Argument, in $2 das zweite Argument und so weiter. Man kann ihnen keinen Wert direkt zuordnen (nur über den Bash-Befehlset).



Von den speziellen Parametern sind $*, $@ und $# für das Arbeiten mit den Stellungsparametern wichtig.

Shell-Skript - Spezielle Parameter •

Den sogenannten speziellen Parametern kann kein Wert zugewiesen werden und sie werden von Shell speziell behandelt: •

$*: Expandiert zu den Stellungsparametern. Werden doppelte Anführungszeichen benutzt ("$*"), so wird zu einem einzigen Wort expandiert. Die Werte der Stellungsparameter sind durch das erste Zeichen von $IFS (normalerweise das Leerzeichen) getrennt (also "$1 $2 $3 ...").



$@: Expandiert zu den Stellungsparametern. Werden doppelte Anführungszeichen benutzt ("$@"), so expandiert jeder Parameter zu einem eigenen Wort (also "$1" "$2" "$3" ...).



$#: Expandiert zur Anzahl der Stellungsparameter.



$?: Expandiert zum Rückgabewert des letzten, im Vordergrund ausgeführten Befehls.



$$: Expandiert zur PID der Shell (auch wenn in einer Subshell verwendet).



$!: Expandiert zur PID des letzten, im Hintergrund gestarteten Befehls.



$0: Expandiert zum Namen der Shell oder des Shell-Skripts.

Shell-Skript - Befehle für Stellungsparameter (set, shift) set - Setzt oder löscht Shell-Optionen bzw. setzt Stellungsparameter. » Wir gehen nicht näher auf die Shell-Optionen ein. » Syntax: set -- Parameter1 Parameter2 ... » $ set -- Param1 "Param 2" $ echo $1 Param1 $ echo $2 Param 2 shift - Entfernt die ersten N Stellungsparameter und rückt die anderen nach. » Falls kein Argument angegeben ist, wird als Anzahl N=1 benutzt. » Das heißt, der Parameter $N+1 wird zu $1, $N+2 wird zu $2 und so weiter. » Hat als Rückgabewert 0, wenn N nicht negativ und kleiner als $# ist.

Technische Praxis der Computersysteme 1 | Wintersemester 2013 | Version: 2013-12-07 10:16

Seite 2 von 10

» $ set -- Param1 "Param 2" "Param 3" $ echo $1 Param1 $ shift 2 $ echo $1 Param 3

Shell-Skript - Beispiele Hier ein Beispiel für ein einfaches Shell-Skript: #!/bin/sh # # Wir werden jetzt einige Stellungsparameter und spezielle Parameter testen. # Dazu dieses Skript mit mindestens drei Argumenten aufrufen. echo Alle Parameter mit \$*: \""$*"\" echo Alle Parameter mit \$@: \""$*"\" echo Anzahl der Parameter: $# echo $1 echo $2 echo $3 shift 3 echo Anzahl der Parameter: $# echo $1 $2 $3 •

In der ersten Zeile ist der Shebang. Dieser muss immer vorhanden sein!



Danach folgen drei Zeilen mit Kommentaren (Shell-Kommentare beginnen mit dem Hash-Zeichen # und gehen dann bis zum Zeilenende).



Anschließend sind die auszuführenden Befehle.

Tests - Befehle •

Die Bash besitzt drei verschiedene Möglichkeiten, um Tests auszuführen: •

test: Eingebauter Bash-Befehl, der es ermöglicht, Zeichenketten oder Zahlen miteinander zu vergleichen bzw. Dateien zu überprüfen. Üblicherweise gibt es auch einen eigenständigen Befehl (in Ubuntu /usr/bin/test).



[ ... ]: Das ist nur eine andere Syntax für den test-Befehl. Man muss dabei die schließende Klammer immer zwingend angeben. Auch dafür gibt es einen eigenständigen Befehl (in Ubuntu /usr/bin/[).



[[ ... ]]: Das ist eine Bash-Erweiterung der obigen Syntax, die die Verwendung angenehmer macht. Dieser Befehl ist eigentlich ein reserviertes Wort der Bash und wird daher von der Bash etwas anders verarbeitet als normale Befehle. Daher muss man Operatoren wie < und && nicht maskieren. Abgesehen davon können die gleichen Tests wie bei den obigen Varianten verwendet werden.

Bezüglich der Unterschiede der drei Möglichkeiten siehe auch BashFAQ/031. •

Verwendet wird wie üblich der Rückgabewert: 0 (wahr) bzw. 1 (falsch).

Technische Praxis der Computersysteme 1 | Wintersemester 2013 | Version: 2013-12-07 10:16

Seite 3 von 10

Tests 1 Hier eine Auflistung der wichtigsten Tests: •



Dateiexistenz und Dateitypen: •

-e Datei → Existiert die Datei?



-d Datei → Existiert die Datei und ist sie ein Verzeichnis?



-f Datei → Existiert die Datei und ist sie eine normale Datei?



-L Datei → Existiert die Datei und ist sie ein symbolischer Link?



-s Datei → Existiert die Datei und ist sie nicht leer?

Zeichenketten: •

-z String → Wahr, falls die Länge der Zeichenkette 0 ist.



-n String → Wahr, falls die Länge der Zeichenkette nicht 0 ist.



String1 = String2 → Wahr, falls beide Zeichenketten gleich sind.



String1 != String2 → Wahr, falls beide Zeichenketten nicht gleich sind.



String1 < String2 → Wahr, falls String1 lexikographisch vor String2 kommt.



String1 > String2 → Wahr, falls String1 lexikographisch nach String2 kommt.

Tests 2 •

Arithmetische Operatoren: Syntax: Argument1 OPERATOR Argument2 •

-eq → Wahr, falls Argument1 gleich Argument2 ist.



-ne → Wahr, falls Argument1 ungleich Argument2 ist.



-lt → Wahr, falls Argument1 kleiner Argument2 ist.



-le → Wahr, falls Argument1 kleiner gleich Argument2 ist.



-gt → Wahr, falls Argument1 größer Argument2 ist.



-ge → Wahr, falls Argument1 größer gleich Argument2 ist.



Für einen Übersicht aller möglichen Tests siehe „CONDITIONAL EXPRESSIONS‘ in der Manpage der Bash oder help test.



Nützlich für Tests sind auch folgende zwei Bash-Befehle: •

true → Hat als Rückgabewert immer 0 (wahr)



false → Hat als Rückgabewert immer 1 (falsch)

Tests - Beispiele •

Überprüfen, ob eine Datei existiert und eine reguläre Datei ist: $ [[ -f Datei ]] && echo ja || echo nein



Vergleichen von Zeichenketten: $ STR1="" $ STR2="hallo" $ STR3="HALLO"

Technische Praxis der Computersysteme 1 | Wintersemester 2013 | Version: 2013-12-07 10:16

Seite 4 von 10

$ [ $STR1 = "" ] && echo ja || echo nein -bash: [: =: unary operator expected nein $ [ "$STR1" = "" ] && echo ja || echo nein ja $ [[ "$STR1" = "" ]] && echo ja || echo nein ja $ [[ $STR2 < $STR3 ]] && echo ja || echo nein ja

Ausgabebefehle (echo, printf) echo - Gibt die Argumente aus. » Optionen: -n → Keinen Zeilenumbruch ausgeben, -e → Auswertung von einigen maskierten Zeichen erlauben (z.B. \n – neue Zeile, \r – Wagenrücklauf, \t – Tabulator), -E → explizit die Auswertung von maskierten Zeichen unterdrücken. » Der Rückgabewert ist immer 0 außer ein Schreibfehler tritt auf. » $ echo a b c a b c $ echo -e "a\tb" a b printf - Formatiert die Argumente basierend auf einem Format. » Syntax: printf [-v Variable] Format [Argumente] » Option -v Variable → die Ausgabe wird der Variablen zugewiesen und nicht auf der Standardausgabe ausgegeben » Format ist eine Formatspezifikation gemäß man 1 printf. » $ printf "%s %5.2f\n" Hallo 5.3236 Hallo 5.33 $ printf "%7.2f\n" 5.333 4.888    5.33    4.89

Eingabebefehle (read) read - Liest eine Zeile von der Standardeingabe und teilt sie in Felder. » Syntax: read [Optionen] [Name ...] » Optionen: -p Prompt → einen Eingabeprompt anzeigen (ohne Zeilenumbruch), -s → Eingabe vom Terminal nicht ausgeben (z.B. für Passworteingabe), -t Timeout → nach einer bestimmten Anzahl von Sekunden die Eingabe abbrechen. » Die eingelesenen Felder werden den angegebenen Variablen zugewiesen, das letzte Feld bekommt alles, was übrig bleibt. Wird kein Variablenname angegeben, wird die Zeile in der Variablen REPLY gespeichert. » $ echo "a b c" | read a b c $ echo $a$b$c $ echo "a b c" | (read a b c && echo $a$b$c) abc $ read -p "Bitte um Eingabe: " -t 5 VAR && echo $VAR || echo Abbruch # keine Eingabe Bitte um Eingabe: Abbruch $ read -p "Bitte um Eingabe: " -t 5 VAR && echo $VAR || echo Abbruch Bitte um Eingabe: Hallo du

Technische Praxis der Computersysteme 1 | Wintersemester 2013 | Version: 2013-12-07 10:16

Seite 5 von 10

Hallo du

if Verzweigung •

if Liste; then Liste; [ elif Liste; then Liste; ] ... [ else Liste; ] fi



Eine if-Verzweigung wird in „Wenn - dann“-Fällen verwendet, d.h. wenn man auf Grund eines Rückgabewerts den Programmablauf entweder in die eine oder in die andere Richtung lenken will.



Eine Liste ist dabei eine Liste von Befehlen wie in „Arbeiten mit der Bash“ definiert. Die Listen bei if und elif entsprechen den Verzweigungsbedingungen.





Die if Liste wird ausgeführt und wenn der Rückgabewert 0 ist, wird die then Liste ausgeführt.



Ansonsten wird jede (optionale) elif Liste der Reihe nach ausgeführt und wenn der Rückgabewert 0 ist, die zugehörige then Liste ausgeführt und der Befehl beendet.



Falls das auch nicht der Fall ist und es eine else Liste gibt, wird diese ausgeführt.



Der Rückgabewert ist der Rückgabewert des letzten ausgeführten Befehls oder 0, falls keine Bedingung wahr war.

if Verzweigung - Beispiel •

Hinweis: Eine Liste kann auch durch einen Zeilenumbruch abgeschlossen werden. Das erlaubt eine schönere Darstellung in einem Shell-Skript.



Als Beispiel werden wir ein Skript schreiben, dass von Fahrenheit in Celsius umrechnet und umgekehrt. Das Skript braucht zwei Argumente: Das erste Argument gibt die Skala für die Temperatur an (Fahrenheit oder Celsius) und das zweite Argument die Grad. Folgende Schritte braucht man für die Berechnung von Celsius aus Fahrenheit: 1. Die Temperatur in Fahrenheit nehmen und davon 32 abziehen. 2. Das Ergebnis durch 1.8 dividieren. 3. Das Ergebnis ist die Temperatur in Celsius. In Formeln zusammengefasst: •

°C = (°F - 32) * 5/9



°F = °C * 9/5 + 32

case Verzweigung •

case Wort in [Muster [ | Muster ] ... ) Liste ;; ] ... esac



Eine case-Verzweigung wird verwendet, wenn es für ein Wort mehrere mögliche Werte gibt und abhängig davon bestimmte Befehle ausgeführt werden sollen.





Das Wort und die Muster werden expandiert und es wird überprüft, ob das expandiert Wort auf ein expandiertes Muster nach den Regeln der Pfadexpansion (siehe „Arbeiten mit der Bash“) passt.

Technische Praxis der Computersysteme 1 | Wintersemester 2013 | Version: 2013-12-07 10:16

Seite 6 von 10





Falls ein Muster passt, wird die entsprechende Liste ausgeführt und kein weiteres Muster wird überprüft (wenn ;; am Ende der Liste benutzt wird, es gibt auch noch andere Möglichkeiten, die wir hier nicht behandeln).



Der Rückgabewert ist 0, wenn kein Muster gepasst hat oder sonst der Rückgabewert des letzten ausgeführten Befehls der Liste.

Wir schreiben nun das vorhin erstelle Programm um und verwenden jetzt case anstatt von if.

for Schleife •

for Name [in Wörter ... ] ; do Liste ; done for (( Ausdruck1 ; Ausdruck2 ; Ausdruck3 )) ; do Liste ; done



Eine for-Schleife wird verwendet, um Befehle eine bestimmte Anzahl an Durchläufen auszuführen.





In der ersten Variante werden die Wörter expandiert. Dann wird die Variable Name der Reihe nach auf die einzelnen Worte gesetzt und jedes Mal die Liste an Befehlen ausgeführt. Wenn die Expansion nichts ergibt, wird als Rückgabewert 0 zurückgegeben, ansonsten ist der Rückgabewert der des letzten ausgeführten Befehls.



In der zweiten Variante wird zuerst Ausdruck1 arithmetische ausgewertet. Ausdruck2 wird solange ausgewertet, bis der Wert 0 herauskommt. Jedes Mal, wenn bei Ausdruck2 ein Wert ungleich 0 herauskommt (entspricht Rückgabewert 0), wird die Liste ausgeführt und Ausdruck3 arithmetisch ausgewertet.



Der Rückgabewert ist der Rückgabewert des letzten Befehls in der Liste oder 1, falls einer der Ausdrücke ungültig sein sollte.

for Schleife - Beispiel •

Als Beispiel werden wir ein Skript schreiben, dass alle Benutzernamen und IDs der Benutzer, die in einer Datei definiert sind, auflistet. Das Skript braucht als Argument die Datei, von der es die Daten lesen soll. Die Datei muss dabei dem Format der Datei /etc/passwd folgen.

while/until Schleife •

while Liste1; do Liste2; done until Liste1; do Liste2; done



Die while-Schleife dient zur Ausführung von Befehlen, solange eine Bedingung wahr ist. Die until -Schleife dient zur Ausführung von Befehlen, bis eine Bedingung wahr ist.





Bei der while-Schleife werden die Befehle in Liste2 wiederholt ausgeführt, solange der letzte Befehl in Liste1 einen Rückgabewert von 0 hat.



Bei der until-Schleife werden die Befehle in Liste2 solange ausgeführt, bis der letzte Befehl in Liste1 einen Rückgabewert von 0 hat.



Der Rückgabewert ist in beiden Fällen der Rückgabewert des letzten ausgeführten Befehls der Liste2, oder 0, falls von Liste2 kein Befehl ausgeführt worden ist.

Technische Praxis der Computersysteme 1 | Wintersemester 2013 | Version: 2013-12-07 10:16

Seite 7 von 10



Wir schreiben nun das vorhin erstelle Programm um und verwenden jetzt while anstatt von for.

Funktionen •

name () Verbundbefehl function name [()] Verbundbefehl



Eine Shell-Funktion erlaubt das Zuweisen eines Namens zu einem Verbundbefehl und dessen Aufruf über diesen Namen (wie einen einfachen Befehl). Die Verwendung des reservierten Worts function ist optional; falls es nicht verwendet wird, muss das Klammernpaar angegeben werden.



Ruft man eine Funktion auf, so werden innerhalb der Funktion die übergebenen Argumente den Stellungsparametern temporär zugewiesen.



Der Rückgabewert einer Funktion, wenn sie ausgeführt wird, ist der Rückgabewert des letzten ausgeführten Befehls.

Funktionen - Beispiele •

Jeder Verbundbefehl ist möglich, normalerweise verwendet man { ... ; }, damit Änderungen an Variablen innerhalb der Funktion auch im Aufrufkontext wirksam sind. $ A=5 $ testit () (A=7; echo Innerhalb: $A) $ echo $A; testit; echo $A 5 Innerhalb: 7 5 $ testit () { A=7; echo Innerhalb: $A ;} $ echo $A; testit; echo $A 5 Innerhalb: 7 7



Das Verwenden von Stellungsparametern funktioniert wie üblich: $ function add { echo Parameter: $@; echo $(($1 + $2)); } $ add 4 6 Parameter: 4 6 10

Hilfreiche Befehle (alias, unalias, unset) alias - Definiert Aliase oder zeigt sie an. » Bash-interner Befehl. » Wird benutzt, um oft eingegebenen Standardoptionen automatisch zu setzen.

Befehlen

kürzere

Namen

zuzuordnen

oder

um

» $ alias ls alias ls='ls --color=auto' $ alias z='ls -alF --color=auto' unalias - Entfernt Aliase. » Bash-interner Befehl.

Technische Praxis der Computersysteme 1 | Wintersemester 2013 | Version: 2013-12-07 10:16

Seite 8 von 10

» $ unalias z $ alias z -bash: alias: z: not found unset - Variablen und Funktionen löschen. » Löscht die angegebenen Variablen oder Funktionen. » $ VAR=5; echo $VAR 5 $ unset VAR; echo $VAR

Hilfreiche Befehle (source, export) source - Führt Befehle von einer Datei in der aktuellen Shell aus. » Syntax: source Dateiname [Argumente] oder . Dateiname [Argumente] » Wird z.B. von Ubuntu für das Setzen von Standardwerten für Startskripte benutzt. » $ echo "$ACTIVE_CONSOLES" $ . /etc/default/console-setup $ echo "$ACTIVE_CONSOLES" /dev/tty[1-6] export - Markiert Variablen für den automatischen Export an Subprozesse. » Wenn eine Variable definiert wird, ist sie nur für die aktuelle Shell und Subshells sichtbar. Erst durch das Markieren mittels export stehen Variablen auch ausgeführten Befehlen zur Verfügung. » Mit der Option -n kann man das Exportieren rückgängig machen. » $ VAR=Wert ; bash -c 'echo $VAR' $ export VAR ; bash -c 'echo $VAR' Wert $ export -n VAR ; bash -c 'echo $VAR'

Hilfreiche Befehle (sleep, timeout) sleep - Wartet für die angegebene Anzahl an Sekunden. » Man kann auch Suffixe verwenden: s für Sekunden (default), m für Minuten, h für Stunden und d für Tage. » Die verwendete Zahl kann auch Kommastellen beinhalten. » $ date +"%S.%N"; sleep 0.05m; date +"%S.%N" 35.919635622 38.924289758 timeout - Bricht einen Befehl nach einer bestimmten Anzahl an Sekunden ab. » Suffixe wie bei sleep möglich (Sekunden sind default). » Optionen: -s Signal → legt das Signal fest (default TERM), das an den Befehl geschickt werden soll. » Der Rückgabewert ist der Rückgabewert des Befehls, oder 124, falls der Befehl abgebrochen wurde. » $ timeout 2 sleep 3 $ echo $? 124

Technische Praxis der Computersysteme 1 | Wintersemester 2013 | Version: 2013-12-07 10:16

Seite 9 von 10

Hilfreiche Befehle (exec, locate) exec - Ersetzt die Shell durch einen Befehl. » Wird öfters in Shell-Skripts verwendet, wenn das Skript am Ende einen Befehl ausführen soll, damit kein unnötiger Shell-Prozess existiert. » $ exec vi locate - Sucht Dateien über ihren Namen. » Benutzt eine Datenbank, durchsucht also im Gegensatz zu find nicht das Dateisystem. Die Datenbank wird üblicherweise regelmäßig durch einen cron-Job aktualisiert. » Es können Shell-Patterns oder reguläre Ausdrücke benutzt werden. Achtung: Falls kein Pattern benutzt wird, wird automatisch nach „Muster“ gesucht! » Optionen: -b → Nur in Dateinamen suchen, nicht im ganzen Pfad » $ locate -b '\passwd' /etc/passwd /etc/cron.daily/passwd /etc/pam.d/passwd /usr/bin/passwd /usr/share/bash-completion/completions/passwd /usr/share/doc/passwd /usr/share/lintian/overrides/passwd

Copyright und Lizenz •

Copyright: Thomas Leitner [email protected]



Basiert teilweise auf den Folien von Harald Schilly [email protected]



Lizenz: Creative Commons CC BY-NC-SA „Namensnennung-Keine kommerzielle Nutzung-Weitergabe unter Österreich.“ - http://creativecommons.org/licenses/by-nc-sa/3.0/at/

gleichen

Technische Praxis der Computersysteme 1 | Wintersemester 2013 | Version: 2013-12-07 10:16

Bedingungen

3.0

Seite 10 von 10