1. Code
  2. Coding Fundamentals
  3. Terminal and CLI

Schreiben eines Shell-Skripts von Grund auf neu

Scroll to top

German (Deutsch) translation by Valentina (you can also view the original English article)

Das Schreiben von Shell-Skripten kann ziemlich entmutigend sein, vor allem, weil die Shell nicht die benutzerfreundlichste Sprache ist. Ich hoffe jedoch, Ihnen in diesem Tutorial zeigen zu können, dass Shell-Scripting nicht so schwierig oder beängstigend ist, wie Sie es vielleicht erwarten.

In diesem Tutorial schreiben wir ein Skript, das die Verwendung des Jasmine-Testframeworks ein wenig vereinfacht. Eigentlich würde ich dieses Skript heute nicht verwenden. Ich würde Grunt.js oder ähnliches verwenden. Ich habe dieses Skript jedoch geschrieben, bevor es Grunt gab, und ich fand, dass das Schreiben eine hervorragende Möglichkeit war, sich mit Shell-Skripten vertraut zu machen. Deshalb verwenden wir es.

Ein Hinweis: Dieses Tutorial ist lose mit meinem bevorstehenden Tuts+ Premium-Kurs "Advanced Command Line Techniques" verbunden. Um mehr über so ziemlich alles in diesem Tutorial zu erfahren, sollten Sie sich auf die Veröffentlichung dieses Kurses einstellen. In diesem Tutorial wird es im Folgenden als "Kurs" bezeichnet.

Unser Drehbuch, das ich jazz nenne, wird also vier Hauptmerkmale haben:

  • Es wird Jasmine aus dem Internet herunterladen, entpacken und den Beispielcode löschen.
  • Es werden JavaScript-Dateien und die zugehörigen Spezifikationsdateien erstellt und mit etwas Vorlagencode vorgefüllt.
  • Die Tests werden im Browser geöffnet.
  • Es wird der Hilfetext angezeigt, der die obigen Punkte umreißt.

Beginnen wir mit der Skriptdatei.


Schritt 1 - Erstellen der Datei

Das Schreiben eines Shell-Skripts ist nur dann nützlich, wenn Sie es vom Terminal aus verwenden können. Um Ihre benutzerdefinierten Skripte auf dem Terminal verwenden zu können, müssen Sie sie in einem Ordner ablegen, der sich in der PATH-Variablen Ihres Terminals befindet (Sie können Ihre PATH-Variable anzeigen, indem Sie echo $PATH ausführen). Ich habe auf meinem Computer einen Ordner ~/bin erstellt (wobei ~ das Ausgangsverzeichnis ist), und dort möchte ich benutzerdefinierte Skripte aufbewahren (wenn Sie dasselbe tun, müssen Sie ihn Ihrem Pfad hinzufügen). Erstellen Sie einfach eine Datei namens jazz und legen Sie sie in Ihrem Ordner ab.

Natürlich müssen wir diese Datei auch ausführbar machen. Andernfalls können wir es nicht ausführen. Wir können dies tun, indem wir den folgenden Befehl ausführen:

1
    chmod +x jazz

Nachdem wir das Skript nun tatsächlich ausführen können, fügen wir einen sehr wichtigen Teil hinzu. Alle Shell-Skripte sollten mit einem shebang beginnen. Wie Wikipedia sagt, sollte dies die erste Zeile des Skripts sein. Es gibt an, mit welchem Interpreter oder welcher Shell dieses Skript ausgeführt werden soll. Wir werden nur eine einfache Standard-Shell verwenden:

1
    #!/bin/sh

Gut, mit all dem, was eingerichtet ist, sind wir bereit, mit dem Schreiben des eigentlichen Codes zu beginnen.


Schritt 2 - Umreißen des Skriptflusses

Zuvor habe ich darauf hingewiesen, welche unterschiedlichen Funktionen unser Shell-Skript haben sollte. Aber woher weiß das Skript, welche Funktion ausgeführt werden soll? Wir werden eine Kombination aus einem Shell-Parameter und einer case-Anweisung verwenden. Wenn Sie das Skript über die Befehlszeile ausführen, verwenden wir einen Unterbefehl wie folgt:

1
    jazz init
2
    jazz create SomeFile
3
    jazz run
4
    jazz help

Dies sollte vertraut aussehen, insbesondere wenn Sie Git verwendet haben:

1
    git init
2
    git status
3
    git commit

Basierend auf diesem ersten Parameter (init, create, run, help) entscheidet unsere case-Anweisung, was ausgeführt werden soll. Wir benötigen jedoch einen Standardfall: Was passiert, wenn kein erster Parameter angegeben wird oder wir einen nicht erkannten ersten Parameter erhalten? In diesen Fällen wird der Hilfetext angezeigt. Also lassen Sie uns anfangen!


Schritt 3 - Schreiben des Hilfetextes

Wir beginnen mit der if-Anweisung, die nach unserem ersten Parameter sucht:

1
    if [ $1 ]
2
    then
3
        # do stuff 

4
    else
5
        # show help

6
    fi

Sie könnten zunächst etwas verwirrt sein, da sich eine if-Anweisung einer Shell von der if-Anweisung einer "normalen" Programmiersprache unterscheidet. Um dies besser zu verstehen, sehen Sie sich den Screencast zu bedingten Anweisungen im Kurs an. Dieser Code prüft, ob ein erster Parameter vorhanden ist ($1). Wenn es da ist, führen wir den then-Code aus; else wird der Hilfetext angezeigt.

Es ist eine gute Idee, den Druck des Hilfetextes in eine Funktion zu packen, da wir ihn mehrmals aufrufen müssen. Wir müssen die Funktion definieren, bevor sie aufgerufen wird, also setzen wir sie oben ein. Ich mag das, weil ich jetzt, sobald ich die Datei öffne, die Dokumentation für das Skript sehe, die eine hilfreiche Erinnerung sein kann, wenn ich zu Code zurückkehre, den Sie seit einiger Zeit nicht mehr gesehen haben. Hier ist ohne weiteres die help-Funktion:

1
    function help () {
2
        echo "jazz - A simple script that makes using the Jasmine testing framework in a standalone project a little simpler."
3
        echo "

4
        echo "    jazz init                  - include jasmine in the project";

5
        echo "    jazz create FunctionName   - creates ./src/FunctionName.js ./spec/FunctionNameSpec.js";

6
        echo "    jazz run                   - runs tests in browser";

7
    }

Ersetzen Sie nun einfach die # show help-Funktion durch einen Aufruf der help-Funktion.

1
    else

2
        help

3
    fi

Schritt 4 - Schreiben der Fallbeschreibung

Wenn es einen ersten Parameter gibt, müssen wir herausfinden, um welchen es sich handelt. Hierfür verwenden wir eine case-Anweisung:

1
	case "$1" in

2
		init)
3
		
4
		;;
5
		create)
6
		
7
		;;
8
		run)
9
		
10
		;;
11
		*)
12
			help
13
		;;
14
	esac

Wir übergeben den ersten Parameter an die case-Anweisung. Dann sollte es mit einem von vier Dingen übereinstimmen: "init", "create", "run" oder unser Wildcard-Standardfall. Beachten Sie, dass wir keinen expliziten "help"-Fall haben: Dies ist nur unser Standardfall. Dies funktioniert, da alles andere als "init", "create" und "run" keine Befehle sind, die wir erkennen. Daher sollte der Hilfetext abgerufen werden.

Jetzt sind wir bereit, den Funktionscode zu schreiben, und wir beginnen mit jazz init.


Schritt 5 - Jasmin mit jazz-init vorbereiten

Der gesamte Code, den wir hier schreiben, wird aus der obigen case-Anweisung in unseren init) case fallen. Der erste Schritt besteht darin, die eigenständige Version von Jasmine herunterzuladen, die in einer Zip-Datei enthalten ist:

1
    echo "Downloading Jasmine ..."
2
    curl -sO $JASMINE_LINK

Wir geben zuerst eine kleine Nachricht wieder und verwenden dann curl, um die Zip-Datei herunterzuladen. Das s-Flag macht es stumm (keine Ausgabe) und das O-Flag speichert den Inhalt der Zip-Datei in einer Datei (andernfalls würde es auf Standard-Out weitergeleitet). Aber was ist mit dieser $JASMINE_LINK-Variablen? Nun, Sie könnten den eigentlichen Link zur Zip-Datei dort einfügen, aber ich bevorzuge es aus zwei Gründen, ihn in eine Variable einzufügen: Erstens hindert er uns daran, einen Teil des Pfades zu wiederholen, wie Sie gleich sehen werden. Zweitens ist es mit dieser Variablen am oberen Rand der Datei einfach, die von uns verwendete Version von Jasmine zu ändern: Ändern Sie einfach diese eine Variable. Hier ist diese Variablendeklaration (ich habe sie außerhalb der if-Anweisung oben platziert):

1
    JASMIME_LINK="http://cloud.github.com/downloads/pivotal/jasmine/jasmine-standalone-1.3.1.zip"

Denken Sie daran, dass in dieser Zeile keine Leerzeichen um das Gleichheitszeichen stehen.

Nachdem wir unsere Zip-Datei haben, können wir sie entpacken und den Inhalt vorbereiten:

1
    unzip -q <code>basename $JASMINE_LINK</code>
2
    rm -rf <code>basename $JASMINE_LINK</code> src/*.js spec/*.js

In zwei dieser Zeilen verwenden wir basename $JASMINE_LINK. Der basename-Befehl reduziert nur einen Pfad auf den Basisnamen: path/to/file.zip wird also nur file.zip. Auf diese Weise können wir diese Variable $JASMINE_LINK verwenden, um auf unsere lokale Zip-Datei zu verweisen.

Nach dem Entpacken löschen wir diese Zip-Datei sowie alle JavaScript-Dateien in den Verzeichnissen src und spec. Dies sind die Beispieldateien, mit denen Jasmine geliefert wird, und wir brauchen sie nicht.

Als nächstes müssen wir uns nur mit einem Mac befassen. Wenn Sie auf einem Mac etwas aus dem Internet herunterladen und versuchen, es zum ersten Mal auszuführen, werden Sie standardmäßig aufgefordert, zu bestätigen, dass Sie es ausführen möchten. Dies liegt an dem erweiterten com.apple.quarantine-Attribut, das Apple in die Datei einfügt. Wir müssen dieses Attribut entfernen.

1
    if which xattr > /dev/null && [ "<code>xattr SpecRunner.html</code>" = "com.apple.quarantine" ]
2
    then

3
        xattr -d com.apple.quarantine SpecRunner.html
4
    fi

Wir prüfen zunächst, ob der xattr-Befehl vorhanden ist, da er auf einigen Unix-Systemen nicht vorhanden ist (ich bin mir nicht sicher, aber möglicherweise handelt es sich um ein Nur-Mac-Programm). Wenn Sie den Kurs-Screencast unter bestimmten Bedingungen gesehen haben, wissen Sie, dass wir jeden Befehl an if übergeben können. Wenn der Exit-Status nicht 0 ist, ist er falsch. Wenn which den xattr-Befehl gefunden wird, wird er mit 0 beendet. Andernfalls wird es mit 1 beendet. In beiden Fällen wird which eine Ausgabe angezeigt. Wir können verhindern, dass dies angezeigt wird, indem wir es nach /dev/null umleiten (dies ist eine spezielle Datei, die alle darauf geschriebenen Daten verwirft).

Dieses doppelte kaufmännische Und ist ein boolesches UND; Es ist für die zweite Bedingung da, die wir überprüfen möchten. Hat die SpecRunner.html dieses Attribut? Wir können einfach den xattr-Befehl für die Datei ausführen und die Ausgabe mit der erwarteten Zeichenfolge vergleichen. (Wir können nicht einfach erwarten, dass die Datei das Attribut enthält, da Sie diese Funktion in Mac OS X tatsächlich deaktivieren können. Beim Versuch, sie zu entfernen, wird eine Fehlermeldung angezeigt, wenn die Datei nicht über das Attribut verfügt.)

Wenn also xattr gefunden wird und die Datei das Attribut hat, entfernen wir es mit dem d-Flag (zum Löschen). Ziemlich einfach, oder?

Der letzte Schritt ist das Bearbeiten von SpecRunner.html. Derzeit enthält es Skript-Tags für die von uns gelöschten JavaScript-Beispieldateien. Wir sollten auch diese Skript-Tags löschen. Ich weiß zufällig, dass diese Skript-Tags die Zeilen 12 bis 18 in den Dateien umfassen. Wir können also den Stream-Editor sed verwenden, um diese Zeilen zu löschen:

1
    sed -i "" '12,18d' SpecRunner.html
2
    echo "Jasmine initialized!"

Das i-Flag weist sed an, die Datei an Ort und Stelle zu bearbeiten oder die Ausgabe des Befehls in derselben Datei zu speichern, die wir übergeben haben. Die leere Zeichenfolge nach dem Flag bedeutet, dass sed die Datei nicht für uns sichern soll. Wenn Sie das möchten, können Sie einfach eine Dateierweiterung in diese Zeichenfolge einfügen (z. B. .bak, um SpecRunner.html.bak abzurufen).

Schließlich werden wir den Benutzer darüber informieren, dass Jasmine initialisiert wurde. Und das war's für unseren jazz-init-Befehl.


Schritt 6 - Erstellen von Dateien mit jazz create

Als Nächstes lassen wir unsere Benutzer JavaScript-Dateien und die zugehörigen Spezifikationsdateien erstellen. Dieser Teil des Codes wird in den Abschnitt "create" der zuvor geschriebenen case-Anweisung eingefügt.

1
    if [ $2 ]
2
    then
3
        # create files

4
    else

5
        echo "please include a name for the file"
6
    fi

Wenn Sie jazz create verwenden, müssen Sie einen Namen für die Datei als zweiten Parameter angeben: beispielsweise jazz create View. Wir werden dies verwenden, um src/View.js und spec/ViewSpec.js zu erstellen. Wenn es also keinen zweiten Parameter gibt, werden wir den Benutzer daran erinnern, einen hinzuzufügen.

Wenn es einen Dateinamen gibt, erstellen wir zunächst diese beiden Dateien (im obigen then Teil oben):

1
    echo "function $2 () {\n\n}" > src/$2.js
2
    echo "describe('$2', function () {\n\n});" > spec/$2Spec.js

Natürlich können Sie alles, was Sie wollen, in Ihre src-Datei einfügen. Ich mache hier etwas Grundlegendes; jazz create View erstellt also src/View.js mit folgendem:

1
function View () {
2
3
}

Sie könnten diese erste echo-Zeile durch folgende ersetzen:

1
    echo "var $2 = (function () {\n\tvar $2Prototype = {\n\n\t};\n\n\treturn {\n\t\tcreate : function (attrs) {\n\t\t\tvar o = Object.create($2Prototype);\n\t\t\textend(o, attrs);\n\t\t\treturn o;\n\t\t}\n \t};\n}());" > src/$2.js

Und dann führt jazz create View dazu:

1
    var View = (function () {
2
        var ViewPrototype = {
3
        
4
        };
5
    
6
        return {
7
            create : function (attrs) {
8
                var o = Object.create(ViewPrototype);
9
                extend(o, attrs);
10
                return o;
11
            }
12
        };
13
    }());

Ihre Vorstellungskraft ist also wirklich die Grenze. Natürlich möchten Sie, dass die Spezifikationsdatei der Standard-Jasmine-Spezifikationscode ist, den ich oben habe. Aber Sie können das auch optimieren, wie Sie möchten.

Der nächste Schritt besteht darin, die Skript-Tags für diese Dateien zu SpecRunner.html hinzuzufügen. Auf den ersten Blick mag dies schwierig erscheinen: Wie können wir programmgesteuert Zeilen in die Mitte einer Datei einfügen? Wieder einmal ist es sed, das den Job macht.

1
    sed -i "" "11a\\

2
    <script src='src/$2.js'></script>\\

3
    <script src='spec/$2Spec.js'></script>

4
    " SpecRunner.html

Wir beginnen genau wie zuvor: In-Place-Bearbeitung ohne Backup. Dann unser Befehl: In Zeile 11 wollen wir die beiden folgenden Zeilen anhängen. Es ist wichtig, die beiden neuen Zeilen zu umgehen, damit sie im Text erscheinen. Wie Sie sehen können, werden nur diese beiden Skript-Tags eingefügt, genau das, was wir für diesen Schritt benötigen.

Wir können mit einer Ausgabe enden:

1
    echo "Created:"
2
    echo "\t- src/$2.js"
3
    echo "\t- spec/$2Spec.js"
4
    echo "Edited:"
5
    echo "\t- SpecRunner.html"

Und das ist jazz create!


Schritt 7 - Ausführen der Spezifikationen mit jazz run

Der letzte Schritt besteht darin, die Tests tatsächlich auszuführen. Dies bedeutet, dass die Datei SpecRunner.html in einem Browser geöffnet wird. Hier gibt es eine kleine Einschränkung. Unter Mac OS X können wir den open-Befehl verwenden, um eine Datei in ihrem Standardprogramm zu öffnen. Dies funktioniert auf keinem anderen Betriebssystem, aber so mache ich es hier. Leider gibt es keinen wirklichen plattformübergreifenden Weg, den ich kenne. Wenn Sie dieses Skript unter Cygwin unter Windows verwenden, können Sie cygstart anstelle von open verwenden. Versuchen Sie andernfalls, "[Ihr Betriebssystem] Shell-Skript Browser öffnen" zu googeln und sehen Sie, was Sie sich einfallen lassen. Leider haben einige Linux-Versionen (zumindest Ubuntu, meiner Erfahrung nach) einen open-Befehl, der für etwas völlig anderes ist. All dies zu sagen, Ihr Kilometerstand mit den folgenden kann variieren.

1
if [ "`which open`" = '/usr/bin/open' ] 
2
then

3
    open SpecRunner.html
4
else

5
    echo "Please open SpecRunner.html in your browser"
6
fi

Inzwischen wissen Sie genau, was das bewirkt: Wenn wir open haben, öffnen wir SpecRunner.html, andernfalls geben wir nur eine Meldung aus, die den Benutzer auffordert, die Datei im Browser zu öffnen.

Ursprünglich sah diese if-Bedingung wie folgt aus:

1
if which open > /dev/null

Wie bei xattr wurde nur die Existenz von open überprüft. Da ich jedoch herausfand, dass es unter Linux einen anderen open-Befehl gibt (sogar auf meinem Ubuntu-Server, der nicht einmal einen Browser öffnen kann!), dachte ich, es wäre möglicherweise besser, den Pfad des open-Programms zu vergleichen, da Linux eine ist bei /bin/open (wieder zumindest auf dem Ubuntu-Server).

All diese zusätzlichen Worte über open klingen vielleicht wie eine Entschuldigung für mein Fehlen einer guten Lösung. Sie weisen tatsächlich auf etwas Wichtiges an der Befehlszeile hin. Verwechseln Sie das Verständnis des Terminals nicht mit dem Verständnis der Konfiguration eines Computers. In diesem Tutorial und dem zugehörigen Kurs haben Sie ein wenig mehr über die Bash-Shell (und die Z-Shell) gelernt. Dies bedeutet jedoch nicht, dass jeder von Ihnen verwendete Computer gleich konfiguriert wird. Es gibt viele Möglichkeiten, neue Befehle (oder verschiedene Versionen von Befehlen) zu installieren und Befehle zu entfernen. Vorbehalt Entwickler.

Nun, das ist das gesamte Drehbuch! Hier ist es wieder alles zusammen:

1
    #! /bin/sh

2
3
    function help () {
4
        echo "jazz - A simple script that makes using the Jasmine testing framework in a standalone project a little simpler."
5
        echo ""
6
        echo "    jazz init                  - include jasmine in the project";
7
        echo "    jazz create FunctionName   - creates ./src/FunctionName.js ./spec/FunctionNameSpec.js";
8
        echo "    jazz run                   - runs tests in browser";
9
    }
10
11
    JASMIME_LINK="http://cloud.github.com/downloads/pivotal/jasmine/jasmine-standalone-1.3.1.zip"
12
13
    if [ $1 ] 
14
    then

15
      case "$1" in

16
      init)
17
        echo "Downloading Jasmine . . ."
18
        curl -sO $JASMIME_LINK 
19
        unzip -q `basename $JASMIME_LINK` 
20
        rm `basename $JASMIME_LINK` src/*.js spec/*.js
21
        
22
        if which xattr > /dev/null && [ "`xattr SpecRunner.html`" = "com.apple.quarantine" ]
23
        then

24
          xattr -d com.apple.quarantine SpecRunner.html
25
        fi

26


27
        sed -i "" "12,18d" SpecRunner.html
28
        echo "Jasmine initialized!"
29
      ;;
30
      create)
31
        if [ $2 ]
32
        then 

33
          echo "function $2 () {\n\n}" > ./src/$2.js
34
          echo "describe('$2', function () {\nit('runs');\n});" > ./spec/$2Spec.js
35
          sed -i "" "11a\\

36
          <script src='src/$2.js'></script>\\

37
          <script src='spec/$2Spec.js'></script>

38
          " SpecRunner.html
39
          echo "Created:"
40
          echo "\t- src/$2.js"
41
          echo "\t- spec/$2Spec.js"
42
          echo "Edited:"
43
          echo "\t- SpecRunner.html"
44
        else

45
          echo 'please add a name for the file'
46
        fi
47
      ;;
48
      "run")
49
        if [ "`which open`" = '/usr/bin/open' ] 
50
        then

51
          open ./SpecRunner.html
52
        else

53
          echo "Please open SpecRunner.html in your browser"
54
        fi
55
      ;;
56
      *)
57
        help;
58
      ;;
59
      esac
60
    else

61
      help;
62
    fi

Na los, probieren Sie es aus!

1
    mkdir project
2
    cd project
3
    jazz init
4
    jazz create Dog
5
    # edit src/Dog.js and spec/DogSpec.js

6
    jazz run

Übrigens, wenn Sie mehr Spaß mit diesem Projekt haben möchten, können Sie es auf Github finden.


Abschluss

Da haben Sie es also! Wir haben gerade ein Shell-Skript für Zwischenstufen geschrieben. das war doch nicht so schlimm, oder? Vergessen Sie nicht, auf meinen bevorstehenden Tuts+ Premium-Kurs gespannt zu bleiben. Sie werden viel mehr über viele der in diesem Artikel verwendeten Techniken sowie über unzählige andere erfahren. Viel Spaß am Terminal!