Die Grundlagen von Bash Scripting
German (Deutsch) translation by Tatsiana Bochkareva (you can also view the original English article)
Shell-Skripte sind in der UNIX-Welt weit verbreitet. Sie eignen sich hervorragend, um sich wiederholende Aufgaben zu beschleunigen und die komplexe Ausführungslogik zu vereinfachen. Sie können so einfach wie eine Reihe von Befehlen sein oder komplexe Aufgaben orchestrieren. In diesem Tutorial erfahren Sie mehr über die Bash-Skriptsprache, indem Sie Schritt für Schritt ein Beispielskript schreiben.
Das Fizz-Buzz-Problem
Eine der besten Möglichkeiten, eine neue Sprache kennenzulernen, ist das Beispiel. Beginnen wir mit einem.
Das Fizz-Buzz-Problem ist sehr einfach. Es wurde berühmt, nachdem ein Programmierer namens Imran es als Interviewtest verwendete. Es stellt sich heraus, dass 90-99,5% der Kandidaten für einen Programmierjob einfach nicht in der Lage sind, das einfachste Programm zu schreiben. Imran nahm dieses einfache Fizz-Buzz-Spiel und bat die Kandidaten, es zu lösen. Viele folgten Imrans Beispiel, und heute ist es eine der am häufigsten gestellten Fragen für einen Programmierjob. Wenn Sie einstellen und eine Möglichkeit benötigen, 90% der Kandidaten zu filtern, ist dies ein großes Problem.
Hier sind die Regeln:
- Nehmen Sie die Zahlen zwischen 1 und 100 und drucken Sie sie aus.
- Wenn eine Zahl durch 3 teilbar ist, drucken Sie "Fizz" anstelle der Zahl.
- Wenn es durch 5 teilbar ist, drucken Sie stattdessen "Buzz".
- Wenn es sowohl durch 3 als auch durch 5 teilbar ist, drucken Sie "FizzBuzz".
Das ist alles dazu. Ich bin sicher, dass die meisten von Ihnen bereits die zwei oder drei if-Anweisungen visualisieren können, um dies zu lösen. Lassen Sie uns dies mit der Skriptsprache Bash durcharbeiten.
Shebang
Ein Shebang bezieht sich auf die Kombination der Hash- und Ausrufezeichen: #!. Der Programmlader sucht in der ersten Zeile des Skripts nach einem Shebang und verwendet den darin angegebenen Interpreter. Ein Shebang besteht aus der folgenden Syntax: #!interpreter[parameter]. Der Dolmetscher ist das Programm, mit dem unsere Sprache interpretiert wird. Für Bash-Skripte wäre das /bin/bash. Wenn Sie beispielsweise ein Skript in PHP erstellen und in der Konsole ausführen möchten, möchten Sie wahrscheinlich /usr/bin/php (oder den Pfad zur ausführbaren PHP-Datei auf Ihrem Computer) als Interpreter verwenden.
1 |
#!/usr/bin/php
|
2 |
<?php |
3 |
phpinfo(); |
Ja, das wird tatsächlich funktionieren! Ist es nicht einfach? Stellen Sie einfach sicher, dass Ihre Datei zuerst ausführbar ist. Sobald Sie dies tun, gibt dieses Skript Ihre PHP-Informationen wie erwartet aus.
Tipp: Um sicherzustellen, dass Ihr Skript auf so vielen Systemen wie möglich funktioniert, können Sie im binbang /bin/env verwenden. Daher können Sie anstelle von /bin/bash /bin/env bash verwenden, was auf Systemen funktioniert, auf denen sich die ausführbare Bash-Datei nicht in /bin befindet.
Text ausgeben
Die Ausgabe eines Skripts entspricht erwartungsgemäß dem, was von Ihrem Befehl ausgegeben wird. Wenn wir jedoch explizit etwas auf den Bildschirm schreiben möchten, können wir echo verwenden.
1 |
#!/bin/bash
|
2 |
|
3 |
echo "Hello World" |
Wenn Sie dieses Skript ausführen, wird "Hello World" in der Konsole gedruckt.
1 |
csaba@csaba ~ $ ./helloWorld.sh
|
2 |
Hello World |
3 |
csaba@csaba ~ $
|
Einführung von Variablen
Wie bei jeder Programmiersprache können Sie beim Schreiben von Shell-Skripten Variablen verwenden.
1 |
#!/bin/bash
|
2 |
|
3 |
message="Hello World" |
4 |
echo $message |
Dieser Code erzeugt genau die gleiche "Hallo Welt" -Nachricht. Wie Sie sehen können, schreiben Sie zum Zuweisen eines Werts zu einer Variablen einfach ihren Namen - schließen Sie das Dollarzeichen davor aus. Seien Sie auch vorsichtig mit Leerzeichen; Zwischen dem Variablennamen und dem Gleichheitszeichen dürfen keine Leerzeichen stehen. Also message="Hello" statt message = 'Hello'
Wenn Sie eine Variable verwenden möchten, können Sie den Wert genau wie im Befehl echo verwenden. Wenn Sie dem Namen der Variablen ein $ voranstellen, wird der Wert zurückgegeben.
Tipp: In Bash-Skripten sind keine Semikolons erforderlich. Sie können sie in den meisten Fällen verwenden, aber seien Sie vorsichtig: Sie haben möglicherweise eine andere Bedeutung als erwartet.
Drucken der Zahlen zwischen 1 und 100
Wenn wir mit unserem Demo-Projekt fortfahren, müssen wir alle Zahlen zwischen 1 und 100 durchlaufen. Dazu müssen wir eine for-Schleife verwenden.
1 |
#!/bin/bash
|
2 |
|
3 |
for number in {1..100}; do |
4 |
echo $number |
5 |
done
|
In diesem Beispiel gibt es einige neue Dinge, die es wert sind, erwähnt zu werden. Dabei werden übrigens alle Zahlen von 1 bis 100 einzeln gedruckt.
- Die
for-Syntax in Bash lautet:for VARIABLE in RANGE; do COMMAND done. - Die geschweiften Klammern verwandeln
1..100in einen Bereich in unserem Beispiel. Sie werden auch in anderen Kontexten verwendet, die wir in Kürze überprüfen werden. -
doundforsind eigentlich zwei separate Befehle. Wenn Sie zwei Befehle in einer einzigen Zeile platzieren möchten, müssen Sie sie irgendwie trennen. Eine Möglichkeit ist die Verwendung von Semikolon. Alternativ können Sie den Code auch ohne Semikolon schreiben, indem Siedoin die folgende Zeile verschieben.
1 |
#!/bin/bash
|
2 |
|
3 |
for number in {1..100} |
4 |
do
|
5 |
echo $number |
6 |
done
|
Die erste Entscheidung
Jetzt, da wir wissen, wie alle Zahlen zwischen 1 und 100 gedruckt werden, ist es Zeit, unsere erste Entscheidung zu treffen.
1 |
#!/bin/bash
|
2 |
|
3 |
for number in {1..100}; do |
4 |
if [ $((number%3)) -eq 0 ]; then |
5 |
echo "Fizz" |
6 |
else
|
7 |
echo $number |
8 |
fi
|
9 |
done
|
In diesem Beispiel wird "Fizz" für durch 3 teilbare Zahlen ausgegeben. Auch hier müssen wir uns mit etwas neuer Syntax befassen. Nehmen wir sie eins nach dem anderen.
-
if..then..else..fi- Dies ist die klassische Syntax für eineif-Anweisung in Bash. Natürlich ist derelse-Teil optional - aber in diesem Fall für unsere Logik erforderlich. -
if COMMAND-RETURN-VALUE; then...-ifwird ausgeführt, wenn der Rückgabewert des Befehls Null ist. Ja, die Logik in Bash basiert auf Null, was bedeutet, dass Befehle, die erfolgreich ausgeführt werden, mit einem Code von 0 beendet werden. Wenn etwas schief geht, wird andererseits eine positive Ganzzahl zurückgegeben. Zur Vereinfachung: Alles andere als 0 wird alsfalseangesehen. - Mathematische Ausdrücke in Bash werden durch doppelte Klammern angegeben.
$((number%3))gibt den verbleibenden Wert der Division der Variablennumberdurch 3 zurück. Bitte beachten Sie, dass wir$nicht in Klammern verwendet haben - nur vor ihnen.
Sie fragen sich möglicherweise, wo sich der Befehl in unserem Beispiel befindet. Gibt es nicht nur eine Klammer mit einem seltsamen Ausdruck? Nun, es stellt sich heraus, dass [ tatsächlich ein ausführbarer Befehl ist. Probieren Sie die folgenden Befehle in Ihrer Konsole aus, um damit herumzuspielen.
1 |
csaba@csaba ~ $ which [ |
2 |
/usr/bin/[ |
3 |
csaba@csaba ~ $ [ 0 -eq 1 ] |
4 |
csaba@csaba ~ $ echo $? |
5 |
1 |
6 |
csaba@csaba ~ $ [ 0 -eq 0 ] |
7 |
csaba@csaba ~ $ echo $? |
8 |
0 |
Tipp: Der Exit-Wert eines Befehls wird immer in die Variable zurückgegeben
?(Fragezeichen). Es wird nach der Ausführung jedes neuen Befehls überschrieben.
Nach Buzz suchen
Bisher geht es uns gut. Wir haben "Fizz"; Jetzt machen wir den "Buzz" Teil.
1 |
#!/bin/bash
|
2 |
|
3 |
for number in {1..100}; do |
4 |
if [ $((number%3)) -eq 0 ]; then |
5 |
echo "Fizz" |
6 |
elif [ $((number%5)) -eq 0 ]; then |
7 |
echo "Buzz" |
8 |
else
|
9 |
echo $number |
10 |
fi
|
11 |
done
|
Oben haben wir eine weitere Bedingung für die Teilbarkeit durch 5 eingeführt: die elif-Anweisung. Dies wird natürlich in else if übersetzt und ausgeführt, wenn der darauf folgende Befehl true (oder 0) zurückgibt. Wie Sie sehen können, werden die bedingten Anweisungen in [] normalerweise mit Hilfe von Parametern, wie -eq ausgewertet, was für "equals" steht.
Für die Syntax
arg1 OP arg2istOPeine von-eq,-ne,-lt,-le,-gtoder-ge. Diese arithmetischen Binäroperatoren gebentruezurück, wennarg1gleich, nicht gleich, kleiner als, kleiner oder gleich, größer als oder größer oder gleicharg2ist.arg1undarg2können positive oder negative ganze Zahlen sein. - Bash-Handbuch
Wenn Sie versuchen, Zeichenfolgen zu vergleichen, können Sie das bekannte == Zeichen verwenden, oder sogar ein einzelnes Gleichheitszeichen reicht aus. != gibt true zurück, wenn die Zeichenfolgen unterschiedlich sind.
Aber der Code ist nicht ganz richtig
Bisher läuft der Code, aber die Logik ist nicht korrekt. Wenn die Zahl durch 3 und 5 teilbar ist, gibt unsere Logik nur "Fizz" aus. Lassen Sie uns unseren Code ändern, um die letzte Anforderung von FizzBuzz zu erfüllen.
1 |
#!/bin/bash
|
2 |
|
3 |
for number in {1..100}; do |
4 |
output="" |
5 |
if [ $((number%3)) -eq 0 ]; then |
6 |
output="Fizz" |
7 |
fi
|
8 |
if [ $((number%5)) -eq 0 ]; then |
9 |
output="${output}Buzz" |
10 |
fi
|
11 |
if [ -z $output ]; then |
12 |
echo $number |
13 |
else
|
14 |
echo $output; |
15 |
fi
|
16 |
done
|
Auch hier mussten wir einige Änderungen vornehmen. Am bemerkenswertesten ist die Einführung einer Variablen und gegebenenfalls die Verkettung von "Buzz". Zeichenfolgen in Bash werden normalerweise zwischen doppelten Anführungszeichen (") definiert. Einfache Anführungszeichen sind ebenfalls verwendbar, aber zur einfacheren Verkettung sind doppelte Anführungszeichen die bessere Wahl. Innerhalb dieser doppelten Anführungszeichen können Sie auf Variablen verweisen: some text $variable some other text" ersetzt $variable durch seinen Inhalt. Wenn Sie Variablen mit Zeichenfolgen ohne Leerzeichen verketten möchten, ziehen Sie es möglicherweise vor, den Namen der Variablen in geschweifte Klammern zu setzen. In den meisten Fällen, wie bei PHP, müssen Sie dies nicht tun, aber es hilft sehr, wenn es um die Lesbarkeit des Codes geht.
Tipp: Sie können leere Zeichenfolgen nicht vergleichen. Das würde einen fehlenden Parameter zurückgeben.
Da Argumente in [ ] als Parameter für "[" behandelt werden, müssen sie sich von einer leeren Zeichenfolge unterscheiden. Obwohl dieser Ausdruck logisch ist, gibt er einen Fehler aus: [$output! = ""]. Aus diesem Grund haben wir [ -z $output] verwendet, das true zurückgibt, wenn der String eine Länge von Null hat.
Extraktionsmethode für logischen Ausdruck
Eine Möglichkeit, unser Beispiel zu verbessern, besteht darin, den mathematischen Ausdruck aus den if-Anweisungen wie folgt in Funktionen zu extrahieren:
1 |
#!/bin/bash
|
2 |
|
3 |
function isDivisibleBy { |
4 |
return $(($1%$2)) |
5 |
}
|
6 |
|
7 |
for number in {1..100}; do |
8 |
output="" |
9 |
if isDivisibleBy $number 3; then |
10 |
output="Fizz" |
11 |
fi
|
12 |
if isDivisibleBy $number 5; then |
13 |
output="${output}Buzz" |
14 |
fi
|
15 |
if [ -z $output ]; then |
16 |
echo $number |
17 |
else
|
18 |
echo $output; |
19 |
fi
|
20 |
done
|
Wir haben die Ausdrücke genommen, um den Rest mit Null zu vergleichen, und sie in eine Funktion verschoben. Darüber hinaus haben wir den Vergleich mit Null eliminiert, weil Null für uns wahr bedeutet. Wir müssen nur den Wert aus dem mathematischen Ausdruck zurückgeben - sehr einfach!
Tipp: Die Definition einer Funktion muss vor dem Aufruf erfolgen.
In Bash können Sie eine Methode als Funktion function func_name { commands; }. Optional gibt es eine zweite Syntax zum Deklarieren von Funktionen: func_name () { commands; }. Wir können also die Zeichenfolge und function löschen und nach dem Namen "()" hinzufügen. Ich persönlich bevorzuge diese Option, wie im obigen Beispiel veranschaulicht. Es ist expliziter und ähnelt traditionellen Programmiersprachen.
Sie müssen die Parameter für eine Funktion in Bash nicht angeben. Das Senden von Parametern an eine Funktion erfolgt durch einfaches Auflisten nach dem durch Leerzeichen getrennten Funktionsaufruf. Setzen Sie keine Kommas oder Klammern in den Funktionsaufruf - es wird nicht funktionieren.
Empfangene Parameter werden Variablen automatisch nach Nummer zugewiesen. Der erste Parameter geht zu $1, der zweite zu $2 und so weiter. Die spezielle Variable $0 bezieht sich auf den Dateinamen des aktuellen Skripts.
Lass uns mit Parametern spielen
1 |
#!/bin/bash
|
2 |
|
3 |
function exampleFunc { |
4 |
echo $1 |
5 |
echo $0 |
6 |
IFS="X" |
7 |
echo "$@" |
8 |
echo "$*" |
9 |
}
|
10 |
|
11 |
exampleFunc "one" "two" "three" |
Dieser Code erzeugt die folgende Ausgabe:
1 |
csaba@csaba ~ $ ./parametersExamples.sh
|
2 |
one |
3 |
./parametersExamples.sh |
4 |
one two thre |
5 |
oneXtwoXthre |
Lassen Sie uns die Quelle Zeile für Zeile analysieren.
- Die letzte Zeile ist der Funktionsaufruf. Wir nennen es mit drei String-Parametern.
- Die erste Zeile nach dem Shebang ist die Funktionsdefinition.
- Die erste Zeile in der Funktion gibt den ersten Parameter aus: "Eins". So weit so einfach.
- In der zweiten Zeile wird der Dateiname des aktuellen Skripts ausgegeben. Wieder sehr einfach.
- In der dritten Zeile wird das Standardzeichentrennzeichen in den Buchstaben "X" geändert. Standardmäßig ist dies " " (ein Leerzeichen). So weiß Bash, wie die Parameter getrennt sind.
- Die vierte Zeile gibt eine spezielle Variable aus,
$@. Es repräsentiert alle Parameter als ein einziges Wort, genau wie im Funktionsaufruf angegeben. - Die letzte Zeile gibt eine weitere spezielle Variable aus,
$*. Es stellt alle Parameter dar, die einzeln genommen und mit dem ersten Buchstaben der IFS-Variablen verknüpft werden. Deshalb ist das ErgebnisoneXtwoXthre.
Zurückgeben von Zeichenfolgen aus Funktionen
Wie bereits erwähnt, können Funktionen in Bash nur Ganzzahlen zurückgeben. Daher wäre das Schreiben der return "a string" ein ungültiger Code. In vielen Situationen benötigen Sie jedoch mehr als nur eine Null oder eine Eins. Wir können unser FizzBuzz-Beispiel so umgestalten, dass wir in der for-Anweisung nur einen Funktionsaufruf ausführen.
1 |
#!/bin/bash
|
2 |
|
3 |
function isDivisibleBy { |
4 |
return $(($1%$2)) |
5 |
}
|
6 |
|
7 |
function fizzOrBuzz { |
8 |
output="" |
9 |
if isDivisibleBy $1 3; then |
10 |
output="Fizz" |
11 |
fi
|
12 |
if isDivisibleBy $1 5; then |
13 |
output="${output}Buzz" |
14 |
fi
|
15 |
if [ -z $output ]; then |
16 |
echo $1 |
17 |
else
|
18 |
echo $output; |
19 |
fi
|
20 |
}
|
21 |
|
22 |
for number in {1..100}; do |
23 |
fizzOrBuzz $number |
24 |
done
|
Nun, das ist der erste Schritt. Wir haben gerade den gesamten Code in eine Funktion namens fizzOrBuzz extrahiert und dann $number durch $1 ersetzt. Alle Ausgaben erfolgen jedoch in der Funktion fizzOrBuzz. Wir möchten aus der for-Schleife eine echo-Anweisung ausgeben, damit wir jeder Zeile eine andere Zeichenfolge voranstellen können. Wir müssen die Ausgabe der fizzOrBuzz-Funktion erfassen.
1 |
#[...]
|
2 |
for number in {1..100}; do |
3 |
echo "-`fizzOrBuzz $number`" |
4 |
fizzBuzzer=$(fizzOrBuzz $number) |
5 |
echo "-${fizzBuzzer}" |
6 |
done
|
Wir haben unsere for-Schleife nur ein wenig aktualisiert (keine weiteren Änderungen). Wir haben jetzt alles zweimal auf zwei verschiedene Arten wiederholt, um die Unterschiede zwischen den beiden Lösungen für dasselbe Problem zu veranschaulichen.
Die erste Lösung, um die Ausgabe einer Funktion oder eines anderen Befehls zu erfassen, ist die Verwendung von Backticks. In 99% der Fälle funktioniert dies einwandfrei. Sie können eine Variable in Backticks einfach anhand ihrer Namen referenzieren, wie wir es mit $number getan haben. Die ersten Zeilen der Ausgabe sollten nun wie folgt aussehen:
1 |
csaba@csaba ~/Personal/Programming/NetTuts/The Basics of BASH Scripting/Sources $ ./fizzBuzz.sh
|
2 |
-1
|
3 |
-1
|
4 |
-2
|
5 |
-2
|
6 |
-Fizz
|
7 |
-Fizz
|
8 |
-4
|
9 |
-4
|
10 |
-Buzz
|
11 |
-Buzz
|
12 |
-Fizz
|
13 |
-Fizz
|
14 |
-7
|
15 |
-7
|
Wie Sie sehen können, ist alles dupliziert. Gleiche Ausgabe.
Für die zweite Lösung haben wir uns entschieden, zuerst den Rückgabewert einer Variablen zuzuweisen. In dieser Zuweisung haben wir $() verwendet, das in diesem Fall das Skript gabelt, den Code ausführt und seine Ausgabe zurückgibt.
;, && und ||
Erinnerst du dich, dass wir hier und da Semikolon verwendet haben? Sie können verwendet werden, um mehrere Befehle auszuführen, die in derselben Zeile geschrieben sind. Wenn Sie sie durch Semikolons trennen, werden sie einfach ausgeführt.
Ein komplexerer Fall ist die Verwendung von && zwischen zwei Befehlen. Ja, das ist ein logisches UND; Dies bedeutet, dass der zweite Befehl nur ausgeführt wird, wenn der erste true zurückgibt (er wird mit 0 beendet). Das ist hilfreich; Wir können die if-Anweisungen in diese Abkürzungen vereinfachen:
1 |
#!/bin/bash
|
2 |
|
3 |
function isDivisibleBy { |
4 |
return $(($1%$2)) |
5 |
}
|
6 |
|
7 |
function fizzOrBuzz { |
8 |
output="" |
9 |
isDivisibleBy $1 3 && output="Fizz" |
10 |
isDivisibleBy $1 5 && output="${output}Buzz" |
11 |
if [ -z $output ]; then |
12 |
echo $1 |
13 |
else
|
14 |
echo $output; |
15 |
fi
|
16 |
}
|
17 |
|
18 |
for number in {1..100}; do |
19 |
echo "-`fizzOrBuzz $number`" |
20 |
done
|
Da unsere Funktion isDivisibleBy einen korrekten Rückgabewert zurückgibt, können wir mit && die gewünschte Variable festlegen. Was nach && ist, wird nur ausgeführt, wenn die Bedingung true ist. Auf die gleiche Weise können wir || verwenden (Doppelpipe-Zeichen) als logisches ODER. Hier ist ein kurzes Beispiel.
1 |
csaba@csaba ~ $ echo "bubu" || echo "bibi" |
2 |
bubu |
3 |
csaba@csaba ~ $ echo false || echo "bibi" |
4 |
false
|
5 |
csaba@csaba ~ $
|
Abschließende Gedanken
Das macht es also für dieses Tutorial! Ich hoffe, Sie haben eine Handvoll neuer Tipps und Techniken zum Schreiben Ihrer eigenen Bash-Skripte erhalten. Vielen Dank fürs Lesen und seien Sie gespannt auf weiterführende Artikel zu diesem Thema.



