|
|
Dieses Dokument ist verfübar auf: English Castellano Deutsch Francais Nederlands Portugues Turkce |
von Katja and Guido Socher <katja(at)linuxfocusorg, guido(at)linuxfocus.org> Über die Autoren: Katja ist die deutsche Redakteurin von LinuxFocus. Sie mag Tux, Film & Fotografie und das Meer. Ihre Homepage findet sich hier. Guido ist ein langjähriger Linuxfan und er mag Linux, weil es von ehrlichen und offenen Leuten entwickelt wurde. Dies ist einer der Gründe, warum wir es Open Source nennen. Seine Homepage ist auf linuxfocus.org/~guido. Übersetzt ins Deutsche von: Katja Socher <katja(at)linuxfocusorg> Inhalt: |
Zusammenfassung:
In diesem Artikel erklären wir, wie man kleine Shellskripte schreibt und geben viele Beispiele.
#!/bin/shDie #! Zeichen sagen dem System, daß das erste Argument, das auf der Zeile folgt, das Programm ist, das benutzt werden soll, um diese Datei auszuführen. In diesem Fall ist /bin/sh die Shell, die wir benutzen.
varname=valueUm den Wert wiederzubekommen, setzt man ein Dollarzeichen vor die Variable:
#!/bin/sh # assign a value: a="hello world" # now print the content of "a": echo "A is:" echo $aTippe diese Zeilen in deinen Texteditor und speichere ihn z.B. als first. Dann mach das Skript durch Tippen von chmod +x first in der Shell ausführbar und starte es dann durch Eingabe von ./first
A is: hello worldManchmal ist es möglich, Variablennamen mit dem Rest des Textes zu vermischen:
num=2 echo "this is the $numnd"Dies druckt nicht "this is the 2nd", sondern "this is the ", weil die Shell nach einer Variablen namens numnd sucht, die keinen Wert hat. Um der Shell zu sagen, daß wir die Variable num meinen, müssen wir geschweifte Klammern benutzen:
num=2 echo "this is the ${num}nd"Dies druckt, was wir wollen: this is the 2nd
Befehlssyntax | Zweck |
---|---|
echo "some text" | schreibt some text auf den Bildschirm |
ls | listet Dateien auf |
wc -l file wc -w file wc -c file |
zählt die Zeilen in einer Datei oder zählt die Wörter in einer Datei oder zählt die Anzahl der Buchstaben |
cp sourcefile destfile | kopiert sourcefile nach destfile |
mv oldname newname | benennt eine Datei um bzw. verschiebt sie |
rm file | löscht eine Datei |
grep 'pattern' file | sucht nach Zeichenketten in einer Datei Beispiel: grep 'searchstring' file.txt |
cut -b colnum file | holt Daten aus Textspalten mit fester Breite Beispiel: hol die Zeichenpositionen 5 bis 9 heraus cut -b5-9 file.txt Verwechsle diesen Befehl nicht mit "cat", der etwas ganz anderes macht |
cat file.txt | schreibt file.txt nach stdout (deinen Bildschirm) |
file somefile | beschreibt, von welchem Typ die Datei somefile ist |
read var | fordert den Benutzer zur Eingabe aus und schreibt sie in eine Variable (var) |
sort file.txt | sortiert Zeilen in file.txt |
uniq | löscht doppelte Zeilen, wird in Kombination mit sort benutzt, da
uniq nur aufeinander folgende doppelte Zeilen entfernt Beispiel: sort file.txt | uniq |
expr | rechnen mit der Shell Beispiel: addiere 2 und 3 expr 2 "+" 3 |
find | sucht nach Dateinamen Beispiel: suche nach Namen: find . -name filename -print Dieser Befehl hat viele verschiedene Möglichkeiten und Optionen. Es ist leider zu viel, um sie alle in diesem Artikel zu erklären. |
tee | schreibt Daten nach stdout (deinen Bildschirm) und in eine Datei Normalerweise wie folgt benutzt: somecommand | tee outfile Es schreibt die Ausgabe von somecommand auf den Bildschirm und in die Datei outfile |
basename file | gibt nur den Dateinamen eines gegebenen Namens zurück und schneidet
den Verzeichnispfad ab Beispiel: basename /bin/tux gibt nur tux zurück |
dirname file | gibt nur das Verzeichnis eines gegebenen Namens zurück und schneidet
den tatsächlichen Dateinamen ab Beispiel: dirname /bin/tux gibt nur /bin zurück |
head file | druckt einige Zeilen vom Dateianfang |
tail file | druckt einige Zeilen vom Dateiende |
sed | sed ist grundsätzlich ein finde und ersetze Programm. Es liest Text
von der Standardeingabe (z.B. von einer pipe) und schreibt das Ergebnis
in stdout (normalerweise der Bildschirm). Das Suchmuster ist ein
regulärer Ausdruck (siehe Referenzen). Diese Suchmuster dürfen nicht mit
der Shellwildcardsyntax verwechselt werden. Um die Zeichenkette
linuxfocus mit LinuxFocus in einer Textdatei zu ersetzen, benutze: cat text.file | sed 's/linuxfocus/LinuxFocus/' > newtext.file Dies ersetzt das erste Auftreten der Zeichenkette linuxfocus in jeder Zeile mit LinuxFocus. Wenn es Zeilen gibt, in denen linuxfocus mehrmals vorkommt und man alle ersetzen will, schreibt man: cat text.file | sed 's/linuxfocus/LinuxFocus/g' > newtext.file |
awk |
Meistens wird awk benutzt, um Felder aus einer Textzeile
herauszuziehen. Der Standard-Feldtrenner ist ein Leerzeichen. Um
einen davon verschiedenen zu spezifizieren, benutzt man die Option -F.
cat file.txt | awk -F, '{print $1 "," $3 }'Hier benutzen wir ein Komma (,) als Feldtrenner und drucken die erste und dritte ($1 $3) Spalte. Wenn file.txt Zeilen hat, wie die folgenden: Adam Bor, 34, India Kerry Miller, 22, USAdann ergibt dies: Adam Bor, India Kerry Miller, USAEs gibt sehr viel mehr, was man mit awk tun kann, aber dies ist eine sehr oft vorkommende Anwendung. |
grep "hello" file.txt | wc -lfindet die Zeilen mit der Zeichenkette hello in file.txt und zählt dann die Zeilen.
find . -mtime -1 -type f -printfindet alle Dateien, die innerhalb der letzen 24 Stunden modifiziert wurden (-mtime -2 wären 48 Stunden). Wenn du alle diese Dateien in ein tar Archiv packen willst (file.tar), würde die Syntax dafür so aussehen:
tar xvf file.tar infile1 infile2 ...Statt alle Dateien einzutippen, kann man die beiden Befehle durch Benutzen von backticks kombinieren (find und tar). Tar packt dann alle Dateien ein, die find gedruckt hat:
#!/bin/sh # The ticks are backticks (`) not normal quotes ('): tar -zcvf lastmod.tar.gz `find . -mtime -1 -type f -print`
if ....; then .... elif ....; then .... else .... fiMeistens wird ein sehr spezieller Befehl namens test innerhalb der if-Anweisung benutzt. Er kann benutzt werden, um Zeichenketten miteinander zu vergleichen oder zu prüfen, ob eine Datei existiert, lesbar ist etc...
[ -f "somefile" ] : Test if somefile is a file. [ -x "/bin/ls" ] : Test if /bin/ls exists and is executable. [ -n "$var" ] : Test if the variable $var contains something [ "$a" = "$b" ] : Test if the variables "$a" and "$b" are equalLaß den Befehl "man test" laufen und du bekommst eine lange Liste mit allen möglichen Testoperatoren für Vergleiche und Dateien.
#!/bin/sh if [ "$SHELL" = "/bin/bash" ]; then echo "your login shell is the bash (bourne again shell)" else echo "your login shell is not bash but $SHELL" fiDie Variable $SHELL enthält den Namen der Loginshell und das ist, was wir hier durch Vergleich mit der Zeichenkette "/bin/bash" testen.
[ -f "/etc/shadow" ] && echo "This computer uses shadow passwors"Das && kann als eine kurze if-Anweisung benutzt werden. Die rechte Seite wird ausgeführt, wenn die linke wahr ist. Du kannst dies als UND lesen. Daher ist ein Beispiel: "Die Datei /etc/shadow existiert UND der Befehl echo wird ausgeführt". Der ODER Operator (||) ist ebenfalls verfügbar. Hier ein Beispiel:
#!/bin/sh mailfolder=/var/spool/mail/james [ -r "$mailfolder" ] || { echo "Can not read $mailfolder" ; exit 1; } echo "$mailfolder has mail from:" grep "^From " $mailfolderDas Skript überprüft zuerst, ob es einen gegebenen Mailfolder lesen kann. Wenn ja, dann druckt es die "From" Zeilen in dem Folder. Wenn es die Datei $mailfolder nicht lesen kann, dann kommt der ODER Operator ins Spiel. In klarem deutsch liest sich der Code als "Mailfolder lesbar oder verlasse das Programm". Das Problem ist hier, daß man genau einen Befehl hinter dem OR haben muß, wir aber zwei brauchen:
case ... in ...) do something here;; esacLaßt uns ein Beispiel anschauen. Der Befehl file kann prüfen, um welchen Dateityp es sich bei einer gegebenen Datei handelt:
file lf.gz ergibt: lf.gz: gzip compressed data, deflated, original filename, last modified: Mon Aug 27 23:09:18 2001, os: UnixWir benutzen dies nun, um ein Skript namens smartzip zu schreiben, das bzip2, gzip and zip komprimierte Dateien automatisch dekomprimieren kann:
#!/bin/sh ftype=`file "$1"` case "$ftype" in "$1: Zip archive"*) unzip "$1" ;; "$1: gzip compressed"*) gunzip "$1" ;; "$1: bzip2 compressed"*) bunzip2 "$1" ;; *) error "File $1 can not be uncompressed with smartzip";; esac
select var in ... ; do break done .... now $var can be used ....Hier ist ein Beispiel:
#!/bin/sh echo "What is your favourite OS?" select var in "Linux" "Gnu Hurd" "Free BSD" "Other"; do break done echo "You have selected $var"Hier ist, was das Skript macht:
What is your favourite OS? 1) Linux 2) Gnu Hurd 3) Free BSD 4) Other #? 1 You have selected LinuxIn der Shell stehen die folgenden Schleifen zur Verfügung:
while ...; do .... doneDie while-Schleife läuft, solange der Ausdruck, den wir testen, wahr ist. Das Schlüsselwort "break" kann benutzt werden, um die Schleife jeder Zeit zu verlassen. Mit dem Schlüsselwort "continue" fährt die Schleife mit der nächsten Wiederholung fort und läßt den Rest des Schleifenkörpers aus.
for var in ....; do .... doneDas folgende gibt z.B. die Buchstaben A-C auf dem Bildschirm aus:
#!/bin/sh for var in A B C ; do echo "var is $var" doneEin nützlicheres Beispielskript, genannt showrpm, gibt eine Zusammenfassung des Inhalts einer Anzahl von RPM-Paketen aus:
#!/bin/sh # list a content summary of a number of RPM packages # USAGE: showrpm rpmfile1 rpmfile2 ... # EXAMPLE: showrpm /cdrom/RedHat/RPMS/*.rpm for rpmpackage in $*; do if [ -r "$rpmpackage" ];then echo "=============== $rpmpackage ==============" rpm -qi -p $rpmpackage else echo "ERROR: cannot read file $rpmpackage" fi doneOben kannst du die nächste spezielle Variable $* sehen, die alle Kommandozeilenargumente enthält. Wenn du
#!/bin/sh echo *.jpgDies gibt "mail.jpg tux.jpg" aus.
#!/bin/sh echo "*.jpg" echo '*.jpg'Dies gibt zweimal "*.jpg" aus.
#!/bin/sh echo $SHELL echo "$SHELL" echo '$SHELL'Dies gibt aus:
/bin/bash /bin/bash $SHELLSchließlich gibt es die Möglichkeit, die spezielle Bedeutung eines einzelnen Zeichens durch das Voranstellen eines Backslashs wegzunehmen:
echo \*.jpg echo \$SHELLDies gibt aus :
*.jpg $SHELLHere documents
#!/bin/sh # we have less than 3 arguments. Print the help text: if [ $# -lt 3 ] ; then cat <<HELP ren -- renames a number of files using sed regular expressions USAGE: ren 'regexp' 'replacement' files... EXAMPLE: rename all *.HTM files in *.html: ren 'HTM$' 'html' *.HTM HELP exit 0 fi OLD="$1" NEW="$2" # The shift command removes one argument from the list of # command line arguments. shift shift # $* contains now all the files: for file in $*; do if [ -f "$file" ] ; then newfile=`echo "$file" | sed "s/${OLD}/${NEW}/g"` if [ -f "$newfile" ]; then echo "ERROR: $newfile exists already" else echo "renaming $file to $newfile ..." mv "$file" "$newfile" fi fi doneDies ist bisher das komplexeste Skript. Laßt es uns ein bißchen diskutieren. Die erste if-Anweisung testet, ob wir mindestens drei Kommandozeilenparameter angegeben haben. (Die spezielle Variable $# enthält die Anzahl der Argumente). Wenn nicht, wird der Hilfetext zu dem Befehl cat geschickt, der ihn wiederum auf dem Bildschirm ausgibt. Nach der Ausgabe des Hilfetextes wird das Programm beendet. Wenn es drei oder mehr Argumente sind, bekommt die Variable OLD den Wert des ersten Arguments zugewiesen und die Varibale NEW den Wert des zweiten. Als nächstes verschieben wir die Kommandozeilenparameter zweimal, um das dritte Argument in die erste Position von $* zu bekommen. Mit $* gehen wir dann in die for Schleife. Jedes Argument in $* wird nun eines nach dem anderen der Variablen $file zugewiesen. Hier testen wir zuerst, ob die Datei wirklich existiert und bilden dann den neuen Dateinamen durch Benutzen von Finden und Ersetzen mit sed. Die backticks werden benutzt, um das Ergebnis der Variable newfile zuzuweisen. Jetzt haben wir alles, was wir brauchen: Den alten Dateinamen und den neuen. Dies wird dann zusammen mit dem Befehl mv zum Umbennen der Dateien benutzt.
functionname() { # inside the body $1 is the first argument given to the function # $2 the second ... body }Man muß Funktionen am Anfang des Skripts "deklarieren", bevor man sie benutzt.
#!/bin/sh # vim: set sw=4 ts=4 et: help() { cat <<HELP xtitlebar -- change the name of an xterm, gnome-terminal or kde konsole USAGE: xtitlebar [-h] "string_for_titelbar" OPTIONS: -h help text EXAMPLE: xtitlebar "cvs" HELP exit 0 } # in case of error or if -h is given we call the function help: [ -z "$1" ] && help [ "$1" = "-h" ] && help # send the escape sequence to change the xterm titelbar: echo -e "\033]0;$1\007" #Es ist eine gute Angewohnheit, immer ausführlichen Hilfetext im Skript zu haben. Dies macht es für andere (und dich) möglich, daß Skript zu benutzen und zu verstehen.
#!/bin/sh help() { cat <<HELP This is a generic command line parser demo. USAGE EXAMPLE: cmdparser -l hello -f -- -somefile1 somefile2 HELP exit 0 } while [ -n "$1" ]; do case $1 in -h) help;shift 1;; # function help is called -f) opt_f=1;shift 1;; # variable opt_f is set -l) opt_l=$2;shift 2;; # -l takes an argument -> shift by 2 --) shift;break;; # end of options -*) echo "error: no such option $1. -h for help";exit 1;; *) break;; esac done echo "opt_f is $opt_f" echo "opt_l is $opt_l" echo "first arg is $1" echo "2nd arg is $2"Probier ihn aus. Du kannst ihn z.B. mit dem folgenden laufen lassen:
cmdparser -l hello -f -- -somefile1 somefile2Dies ergibt
opt_f is 1 opt_l is hello first arg is -somefile1 2nd arg is somefile2Wie arbeitet das Programm? Grundsätzlich läuft die Schleife durch alle Argumente und vergleicht sie mit der case-Anweisung. Wenn es ein übereinstimmendes findet, setzt es eine Variable und verschiebt die Kommandozeile um eins. Die Unixkonvention ist, daß Optionen (Dinge, die mit einem Minus anfangen) zuerst kommen müssen. Man kann das Ende einer Option durch zwei Minuszeichen anzeigen (--). Dies braucht man z.B. bei grep, um nach einer Zeichenkette zu suchen, die mit einem Minus anfängt:
Search for -xx- in file f.txt: grep -- -xx- f.txtUnser Optionenparser kann ebenfalls die zwei Minuszeichen handhaben, wie du im obigen Listing sehen kannst.
cp framework.sh myscriptund fügst dann die aktuelle Funktionalität in "myscript" ein.
#!/bin/sh # vim: set sw=4 ts=4 et: help() { cat <<HELP b2h -- convert binary to decimal USAGE: b2h [-h] binarynum OPTIONS: -h help text EXAMPLE: b2h 111010 will return 58 HELP exit 0 } error() { # print an error and exit echo "$1" exit 1 } lastchar() { # return the last character of a string in $rval if [ -z "$1" ]; then # empty string rval="" return fi # wc puts some space behind the output this is why we need sed: numofchar=`echo -n "$1" | wc -c | sed 's/ //g' ` # now cut out the last char rval=`echo -n "$1" | cut -b $numofchar` } chop() { # remove the last character in string and return it in $rval if [ -z "$1" ]; then # empty string rval="" return fi # wc puts some space behind the output this is why we need sed: numofchar=`echo -n "$1" | wc -c | sed 's/ //g' ` if [ "$numofchar" = "1" ]; then # only one char in string rval="" return fi numofcharminus1=`expr $numofchar "-" 1` # now cut all but the last char: rval=`echo -n "$1" | cut -b 0-${numofcharminus1}` } while [ -n "$1" ]; do case $1 in -h) help;shift 1;; # function help is called --) shift;break;; # end of options -*) error "error: no such option $1. -h for help";; *) break;; esac done # The main program sum=0 weight=1 # one arg must be given: [ -z "$1" ] && help binnum="$1" binnumorig="$1" while [ -n "$binnum" ]; do lastchar "$binnum" if [ "$rval" = "1" ]; then sum=`expr "$weight" "+" "$sum"` fi # remove the last position in $binnum chop "$binnum" binnum="$rval" weight=`expr "$weight" "*" 2` done echo "binary $binnumorig is decimal $sum" #Der in diesem Skript benutzte Algorithmus nimmt die dezimale Wertigkeit (1,2,4,8,16,..) jeder Ziffer beginnend mit der am weitesten rechts liegenden und addiert sie dann zu der Summe, wenn die Ziffer eine 1 ist. "10" ist daher:
#!/bin/sh # vim: set sw=4 ts=4 et: ver="0.1" help() { cat <<HELP rotatefile -- rotate the file name USAGE: rotatefile [-h] filename OPTIONS: -h help text EXAMPLE: rotatefile out This will e.g rename out.2 to out.3, out.1 to out.2, out to out.1 and create an empty out-file The max number is 10 version $ver HELP exit 0 } error() { echo "$1" exit 1 } while [ -n "$1" ]; do case $1 in -h) help;shift 1;; --) break;; -*) echo "error: no such option $1. -h for help";exit 1;; *) break;; esac done # input check: if [ -z "$1" ] ; then error "ERROR: you must specify a file, use -h for help" fi filen="$1" # rename any .1 , .2 etc file: for n in 9 8 7 6 5 4 3 2 1; do if [ -f "$filen.$n" ]; then p=`expr $n + 1` echo "mv $filen.$n $filen.$p" mv $filen.$n $filen.$p fi done # rename the original file: if [ -f "$filen" ]; then echo "mv $filen $filen.1" mv $filen $filen.1 fi echo touch $filen touch $filenWie arbeitet das Programm? Nach der Überprüfung, ob der Benutzer einen Dateinamen eingegeben hat, geht es in eine for-Schleife, die von 9 nach 1 runterzählt. Datei 9 wird nun in 10, Datei 8 in 9 usw. umbenannt. Nach der Schleife nennen wir die Originaldatei in 1 um und erzeugen eine leere Datei mit dem Namen der Originaldatei.
sh -x strangescriptDies führt das Skript aus und zeigt alle Anweisungen, die ausgeführt werden mit bereits ersetzen Variablen und Wildcards an.
sh -n your_scriptlaufen. Wenn nichst auf dem Bildschirm ausgegeben wird, ist das Programm frei von Syntaxfehlern.
|
Der LinuxFocus Redaktion schreiben
© Katja and Guido Socher, FDL LinuxFocus.org Einen Fehler melden oder einen Kommentar an LinuxFocus schicken |
Autoren und Übersetzer:
|
2001-12-30, generated by lfparser version 2.23