Scroll to top

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

Das Schreiben von Shell-Skripten ähnelt starkdem Programmieren. Einige Skripte erfordern wenig Zeitaufwand. Andere komplexe Skripte erfordern möglicherweise Gedanken, Planung und ein größeres Engagement. Aus dieser Perspektive ist es sinnvoll, einen testgetriebenen Ansatz zu wählen und unsere Shell-Skripte zu testen.

Um dieses Tutorial optimal nutzen zu können, müssen Sie mit der Befehlszeilenschnittstelle (Command Line Interface, CLI) vertraut sein. Wenn Sie eine Auffrischung benötigen, sollten Sie sich das Tutorial Die Befehlszeile ist Ihr bester Freund ansehen. Sie benötigen außerdem ein grundlegendes Verständnis der Bash-ähnlichen Shell-Skripterstellung. Schließlich möchten Sie sich vielleicht mit den TDD-Konzepten (Test Driven Development) und Unit-Tests im Allgemeinen vertraut machen. Lesen Sie unbedingt diese testgesteuerten PHP-Tutorials, um die Grundidee zu erhalten.


Bereiten Sie die Programmierumgebung vor

Zunächst benötigen Sie einen Texteditor, um Ihre Shell-Skripte und Unit-Tests zu schreiben. Verwenden Sie Ihren Favoriten!

Wir werden das shUnit2-Shell-Unit-Test-Framework verwenden, um unsere Unit-Tests durchzuführen. Es wurde für Bash-ähnliche Muscheln entwickelt und funktioniert mit diesen. shUnit2 ist ein Open Source-Framework, das unter der GPL-Lizenz veröffentlicht wurde. Eine Kopie des Frameworks ist auch im Beispielquellcode dieses Tutorials enthalten.

Die Installation von shUnit2 ist sehr einfach. Laden Sie das Archiv einfach herunter und extrahieren Sie es an einen beliebigen Ort auf Ihrer Festplatte. Es ist in Bash geschrieben und als solches besteht das Framework nur aus Skriptdateien. Wenn Sie shUnit2 häufig verwenden möchten, empfehle ich dringend, dass Sie es an einer Stelle in Ihrem PATH ablegen.


Schreiben Sie unseren ersten Test

Extrahieren Sie für dieses Lernprogramm shUnit in ein Verzeichnis mit demselben Namen in Ihrem Sources ordner (siehe den diesem Lernprogramm beigefügten Code). Erstellen Sie einen Tests ordner in Sources und fügen Sie einen neuen Dateiaufruf firstTest.sh hinzu.

1
#! /usr/bin/env sh

2
3
### firstTest.sh ###

4
5
function testWeCanWriteTests () {
6
	assertEquals "it works" "it works"
7
}
8
9
## Call and Run all Tests

10
. "../shunit2-2.1.6/src/shunit2"

Dann machen Sie Ihre Testdatei ausführbar.

1
$ cd __your_code_folder__/Tests
2
$ chmod +x firstTest.sh

Jetzt können Sie es einfach ausführen und die Ausgabe beobachten:

1
$ ./firstTest.sh
2
testWeCanWriteTests
3
4
Ran 1 test.
5
6
OK

Es heißt, wir haben einen erfolgreichen Test durchgeführt. Lassen Sie uns nun den Test fehlschlagen. Ändern Sie die assertEquals-Anweisung so, dass die beiden Zeichenfolgen nicht identisch sind, und führen Sie den Test erneut aus:

1
$ ./firstTest.sh
2
testWeCanWriteTests
3
ASSERT:expected:<it works> but was:<it does not work>
4
5
Ran 1 test.
6
7
FAILED (failures=1)

Ein Tennisspiel

Sie schreiben Abnahmetests z Beginn eines project/feature/story, wenn Sie eine bestimmte Anforderung klar definieren können.

Nachdem wir eine funktionierende Testumgebung haben, schreiben wir ein Skript, das eine Datei liest, Entscheidungen basierend auf dem Inhalt der Datei trifft und Informationen auf dem Bildschirm ausgibt.

Das Hauptziel des Drehbuchs ist es, die Punktzahl eines Tennisspiels zwischen zwei Spielern anzuzeigen. Wir werden uns nur darauf konzentrieren, die Punktzahl eines einzelnen Spiels zu halten. alles andere liegt bei dir. Die Bewertungsregeln sind:

  • Zu Beginn hat jeder Spieler eine Punktzahl von Null, die als "love" bezeichnet wird.
  • Der erste, zweite und dritte gewonnene Ball wird als "fifteen", "thirty", und "forty" markiert.
  • Wenn bei "forty" die Punktzahl gleich ist, wird sie "deuce" genannt.
  • Danach wird die Punktzahl als "Advantage" für den Spieler beibehalten, der einen Punkt mehr als der andere Spieler erzielt.
  • Ein Spieler ist der Gewinner, wenn er einen Vorteil von mindestens zwei Punkten hat und mindestens drei Punkte gewinnt (d.h. wenn er mindestens "forty" erreicht hat).

Definition von Input und Output

Unsere Anwendung liest die Partitur aus einer Datei. Ein anderes System überträgt die Informationen in diese Datei. Die erste Zeile dieser Datendatei enthält die Namen der Spieler. Wenn ein Spieler einen Punkt erzielt, steht sein Name am Ende der Datei. Eine typische Score-Datei sieht folgendermaßen aus:

1
John - Michael
2
John
3
John
4
Michael
5
John
6
Michael
7
Michael
8
John
9
John

Sie finden diesen Inhalt in der Datei input.txt im Ordner Source.

Die Ausgabe unseres Programms schreibt die Partitur zeilenweise auf den Bildschirm. Die Ausgabe sollte sein:

1
John - Michael
2
John: 15 - Michael: 0
3
John: 30 - Michael: 0
4
John: 30 - Michael: 15
5
John: 40 - Michael: 15
6
John: 40 - Michael: 30
7
Deuce
8
John: Advantage
9
John: Winner

Diese Ausgabe befindet sich auch in der Datei output.txt. Wir werden diese Informationen verwenden, um zu überprüfen, ob unser Programm korrekt ist.


Der Abnahmetest

Sie schreiben Abnahmetests zu Beginn eines project/feature/story, wenn Sie eine bestimmte Anforderung klar definieren können. In unserem Fall ruft dieser Test einfach unser in Kürze zu erstellendes Skript mit dem Namen der Eingabedatei als Parameter auf und erwartet, dass die Ausgabe mit der handgeschriebenen Datei aus dem vorherigen Abschnitt identisch ist:

1
#! /usr/bin/env sh

2
3
### acceptanceTest.sh ###

4
5
function testItCanProvideAllTheScores () {
6
	cd ..
7
	./tennisGame.sh ./input.txt > ./results.txt
8
	diff ./output.txt ./results.txt
9
	assertTrue 'Expected output differs.' $?
10
}
11
12
## Call and Run all Tests

13
. "../shunit2-2.1.6/src/shunit2"

Wir werden unsere Tests im Ordner Source/Tests ausführen. Daher führt uns cd .. in das Source verzeichnis. Dann wird versucht, die noch nicht vorhandene tennisGamse.sh auszuführen. Dann vergleicht der Befehl diff die beiden Dateien: ./output.txt ist unsere handgeschriebene Ausgabe und ./results.txt enthält das Ergebnis unseres Skripts. Schließlich überprüft assertTrue den Exit-Wert von diff.

Im Moment gibt unser Test jedoch den folgenden Fehler zurück:

1
$ ./acceptanceTest.sh
2
testItCanProvideAllTheScores
3
./acceptanceTest.sh: line 7: tennisGame.sh: command not found
4
diff: ./results.txt: No such file or directory
5
ASSERT:Expected output differs.
6
7
Ran 1 test.
8
9
FAILED (failures=1)

Lassen Sie uns diese Fehler in einen netten Fehler verwandeln, indem wir eine leere Datei namens tennisGame.sh erstellen und sie ausführbar machen. Wenn wir jetzt unseren Test ausführen, erhalten wir keinen Fehler:

1
./acceptanceTest.sh
2
testItCanProvideAllTheScores
3
1,9d0
4
< John - Michael
5
< John: 15 - Michael: 0
6
< John: 30 - Michael: 0
7
< John: 30 - Michael: 15
8
< John: 40 - Michael: 15
9
< John: 40 - Michael: 30
10
< Deuce
11
< John: Advantage
12
< John: Winner
13
ASSERT:Expected output differs.
14
15
Ran 1 test.
16
17
FAILED (failures=1)

Implementierung mit TDD

Erstellen Sie eine weitere Datei mit dem Namen unitTests.sh für unsere Komponententests. Wir möchten unser Skript nicht für jeden Test ausführen. Wir möchten nur die Funktionen ausführen, die wir testen. Also werden wir tennisGame.sh nur die Funktionen ausführen lassen, die sich in functions.sh befinden:

1
#! /usr/bin/env sh

2
3
### unitTest.sh ###

4
5
source ../functions.sh
6
7
function testItCanProvideFirstPlayersName () {
8
	assertEquals 'John' `getFirstPlayerFrom 'John - Michael'`
9
}
10
11
## Call and Run all Tests

12
. "../shunit2-2.1.6/src/shunit2"

Unser erster Test ist einfach. Wir versuchen, den Namen des ersten Spielers abzurufen, wenn eine Zeile zwei Namen enthält, die durch einen Bindestrich getrennt sind. Dieser Test schlägt fehl, da wir noch keine Funktion getFirstPlayerFrom haben:

1
$ ./unitTest.sh
2
testItCanProvideFirstPlayersName
3
./unitTest.sh: line 8: getFirstPlayerFrom: command not found
4
shunit2:ERROR assertEquals() requires two or three arguments; 1 given
5
shunit2:ERROR 1: John 2:  3:
6
7
Ran 1 test.
8
9
OK

Die Implementierung für getFirstPlayerFrom ist sehr einfach. Es ist ein regulärer Ausdruck, der durch den Befehl sed gedrückt wird:

1
### functions.sh ###

2
3
function getFirstPlayerFrom () {
4
	echo $1 | sed -e 's/-.*//'
5
}

Jetzt besteht der Test:

1
$ ./unitTest.sh
2
testItCanProvideFirstPlayersName
3
4
Ran 1 test.
5
6
OK

Schreiben wir einen weiteren Test für den Namen des zweiten Spielers:

1
### unitTest.sh ###

2
3
[...]
4
5
function testItCanProvideSecondPlayersName () {
6
	assertEquals 'Michael' `getSecondPlayerFrom 'John - Michael'`
7
}

Der Fehlschlag:

1
./unitTest.sh
2
testItCanProvideFirstPlayersName
3
testItCanProvideSecondPlayersName
4
ASSERT:expected:<Michael> but was:<John>
5
6
Ran 2 tests.
7
8
FAILED (failures=1)

Und jetzt die Funktionsimplementierung, um es zu bestehen:

1
### functions.sh ###

2
3
[...]
4
5
function getSecondPlayerFrom () {
6
	echo $1 | sed -e 's/.*-//'
7
}

Jetzt haben wir Tests bestanden:

1
$ ./unitTest.sh
2
testItCanProvideFirstPlayersName
3
testItCanProvideSecondPlayersName
4
5
Ran 2 tests.
6
7
OK

Lassen Sie uns die Dinge beschleunigen

Ab diesem Punkt werden wir einen Test und die Implementierung schreiben, und ich werde nur erklären, was es verdient, erwähnt zu werden.

Lassen Sie uns testen, ob wir einen Spieler mit nur einer Punktzahl haben. Der folgende Test wurde hinzugefügt:

1
function testItCanGetScoreForAPlayerWithOnlyOneWin () {
2
	standings=$'John - Michael\nJohn'
3
	assertEquals '1' `getScoreFor 'John' "$standings"`
4
}

Und die Lösung:

1
function getScoreFor () {
2
	player=$1
3
	standings=$2
4
	totalMatches=$(echo "$standings" | grep $player | wc -l)
5
	echo $(($totalMatches-1))
6
}

Wir verwenden einige Fancy-Pants-Anführungszeichen, um die Zeilenumbruchsequenz (\n) innerhalb eines String-Parameters zu übergeben. Dann verwenden wir grep, um die Zeilen zu finden, die den Namen des Spielers enthalten, und zählen sie mit wc. Schließlich subtrahieren wir eine vom Ergebnis, um dem Vorhandensein der ersten Zeile entgegenzuwirken (sie enthält nur nicht mit der Punktzahl verbundene Daten).

Jetzt befinden wir uns in der Refactoring-Phase von TDD.

Ich habe gerade festgestellt, dass der Code tatsächlich für mehr als einen Punkt pro Spieler funktioniert, und wir können unsere Tests umgestalten, um dies widerzuspiegeln. Ändern Sie die obige Testfunktion wie folgt:

1
function testItCanGetScoreForAPlayer () {
2
	standings=$'John - Michael\nJohn\nMichael\nJohn'
3
	assertEquals '2' `getScoreFor 'John' "$standings"`
4
}

Die Tests bestehen noch. Zeit, mit unserer Logik fortzufahren:

1
function testItCanOutputScoreAsInTennisForFirstPoint () {
2
	assertEquals 'John: 15 - Michael: 0' "`displayScore 'John' 1 'Michael' 0`"
3
}

Und die Umsetzung:

1
function displayScore () {
2
	if [ "$2" -eq '1' ]; then

3
		playerOneScore='15'
4
	fi

5


6
	echo "$1: $playerOneScore - $3: $4"
7
}

Ich überprüfe nur den zweiten Parameter. Das sieht so aus, als würde ich schummeln, aber es ist der einfachste Code, um den Test zu bestehen. Das Schreiben eines weiteren Tests zwingt uns, mehr Logik hinzuzufügen, aber welchen Test sollten wir als nächstes schreiben?

Wir können zwei Wege gehen. Wenn wir testen, ob der zweite Spieler einen Punkt erhält, müssen wir eine weitere if-Anweisung schreiben. Wir müssen jedoch nur eine else-Anweisung hinzufügen, wenn wir den zweiten Punkt des ersten Spielers testen möchten. Letzteres impliziert eine einfachere Implementierung. Versuchen wir Folgendes:

1
function testItCanOutputScoreAsInTennisForSecondPointFirstPlayer () {
2
	assertEquals 'John: 30 - Michael: 0' "`displayScore 'John' 2 'Michael' 0`"
3
}

Und die Umsetzung:

1
function displayScore () {
2
	if [ "$2" -eq '1' ]; then

3
		playerOneScore='15'
4
	else

5
		playerOneScore='30'
6
	fi

7


8
	echo "$1: $playerOneScore - $3: $4"
9
}

Das sieht immer noch betrügerisch aus, funktioniert aber perfekt. Weiter zum dritten Punkt:

1
function testItCanOutputScoreAsInTennisForTHIRDPointFirstPlayer () {
2
	assertEquals 'John: 40 - Michael: 0' "`displayScore 'John' 3 'Michael' 0`"
3
}

Die Umsetzung:

1
function displayScore () {
2
	if [ "$2" -eq '1' ]; then

3
		playerOneScore='15'
4
	elif [ "$2" -eq '2' ]; then

5
		playerOneScore='30'
6
	else

7
		playerOneScore='40'
8
	fi

9


10
	echo "$1: $playerOneScore - $3: $4"
11
}

Dieses if-elif-else fängt an mich zu ärgern. Ich möchte es ändern, aber lassen Sie uns zuerst unsere Tests überarbeiten. Wir haben drei sehr ähnliche Tests; Schreiben wir sie also in einen einzigen Test, der drei Aussagen macht:

1
function testItCanOutputScoreWhenFirstPlayerWinsFirst3Points () {
2
	assertEquals 'John: 15 - Michael: 0' "`displayScore 'John' 1 'Michael' 0`"
3
	assertEquals 'John: 30 - Michael: 0' "`displayScore 'John' 2 'Michael' 0`"
4
	assertEquals 'John: 40 - Michael: 0' "`displayScore 'John' 3 'Michael' 0`"
5
}

Das ist besser und es geht immer noch vorbei. Erstellen wir nun einen ähnlichen Test für den zweiten Spieler:

1
function testItCanOutputScoreWhenSecondPlayerWinsFirst3Points () {
2
	assertEquals 'John: 0 - Michael: 15' "`displayScore 'John' 0 'Michael' 1`"
3
	assertEquals 'John: 0 - Michael: 30' "`displayScore 'John' 0 'Michael' 2`"
4
	assertEquals 'John: 0 - Michael: 40' "`displayScore 'John' 0 'Michael' 3`"
5
}

Das Ausführen dieses Tests führt zu einer interessanten Ausgabe:

1
testItCanOutputScoreWhenSecondPlayerWinsFirst3Points
2
ASSERT:expected:<John: 0 - Michael: 15> but was:<John: 40 - Michael: 1>
3
ASSERT:expected:<John: 0 - Michael: 30> but was:<John: 40 - Michael: 2>
4
ASSERT:expected:<John: 0 - Michael: 40> but was:<John: 40 - Michael: 3>

Nun, das war unerwartet. Wir wussten, dass Michael falsche Ergebnisse haben würde. Die Überraschung ist John; er sollte 0 haben, nicht 40. Lassen Sie uns das beheben, indem Sie zuerst den if-elif-else-Ausdruck ändern:

1
function displayScore () {
2
	if [ "$2" -eq '1' ]; then

3
		playerOneScore='15'
4
	elif [ "$2" -eq '2' ]; then

5
		playerOneScore='30'
6
	elif [ "$2" -eq '3' ]; then

7
		playerOneScore='40'
8
	else

9
		playerOneScore=$2
10
	fi

11


12
	echo "$1: $playerOneScore - $3: $4"
13
}

Das if-elif-else ist jetzt komplexer, aber wir haben zumindest die Punktzahlen von John festgelegt:

1
testItCanOutputScoreWhenSecondPlayerWinsFirst3Points
2
ASSERT:expected:<John: 0 - Michael: 15> but was:<John: 0 - Michael: 1>
3
ASSERT:expected:<John: 0 - Michael: 30> but was:<John: 0 - Michael: 2>
4
ASSERT:expected:<John: 0 - Michael: 40> but was:<John: 0 - Michael: 3>

Jetzt lass uns Michael reparieren:

1
function displayScore () {
2
	echo "$1: `convertToTennisScore $2` - $3: `convertToTennisScore $4`"
3
}
4
5
function convertToTennisScore () {
6
	if [ "$1" -eq '1' ]; then

7
		playerOneScore='15'
8
	elif [ "$1" -eq '2' ]; then

9
		playerOneScore='30'
10
	elif [ "$1" -eq '3' ]; then

11
		playerOneScore='40'
12
	else

13
		playerOneScore=$1
14
	fi

15


16
	echo $playerOneScore;
17
}

Das hat gut funktioniert! Jetzt ist es Zeit, diesen hässlichen if-elif-else-Ausdruck endlich umzugestalten:

1
function convertToTennisScore () {
2
	declare -a scoreMap=('0' '15' '30' '40')
3
	echo ${scoreMap[$1]};
4
}

Wertkarten sind wunderbar! Kommen wir zum Fall "Deuce":

1
function testItSayDeuceWhenPlayersAreEqualAndHaveEnoughPoinst () {
2
	assertEquals 'Deuce' "`displayScore 'John' 3 'Michael' 3`"
3
}

Wir suchen nach "Deuce", wenn alle Spieler mindestens 40 Punkte haben.

1
function displayScore () {
2
	if [ $2 -gt 2 ] && [ $4 -gt 2 ] && [ $2 -eq $4 ]; then

3
		echo "Deuce"
4
	else

5
		echo "$1: `convertToTennisScore $2` - $3: `convertToTennisScore $4`"
6
	fi
7
}

Jetzt testen wir den Vorteil des ersten Spielers:

1
function testItCanOutputAdvantageForFirstPlayer () {
2
	assertEquals 'John: Advantage' "`displayScore 'John' 4 'Michael' 3`"
3
}

Und um es zu schaffen:

1
function displayScore () {
2
	if [ $2 -gt 2 ] && [ $4 -gt 2 ] && [ $2 -eq $4 ]; then

3
		echo "Deuce"
4
	elif [ $2 -gt 2 ] && [ $4 -gt 2 ] && [ $2 -gt $4 ]; then

5
		echo "$1: Advantage"
6
	else

7
		echo "$1: `convertToTennisScore $2` - $3: `convertToTennisScore $4`"
8
	fi
9
}

Es gibt wieder dieses hässliche if-elif-else, und wir haben auch viele Überschneidungen. Alle unsere Tests bestehen, also überarbeiten wir:

1
function displayScore () {
2
	if outOfRegularScore $2 $4 ; then

3
		checkEquality $2 $4
4
		checkFirstPlayerAdv $1 $2 $4
5
	else

6
		echo "$1: `convertToTennisScore $2` - $3: `convertToTennisScore $4`"
7
	fi
8
}
9
10
function outOfRegularScore () {
11
	[ $1 -gt 2 ] && [ $2 -gt 2 ]
12
	return $?
13
}
14
15
function checkEquality () {
16
	if [ $1 -eq $2 ]; then

17
		echo "Deuce"
18
	fi
19
}
20
21
function checkFirstPlayerAdv () {
22
	if [ $2 -gt $3 ]; then

23
		echo "$1: Advantage"
24
	fi
25
}

Das wird jetzt funktionieren. Testen wir den Vorteil für den zweiten Spieler:

1
function testItCanOutputAdvantageForSecondPlayer () {
2
	assertEquals 'Michael: Advantage' "`displayScore 'John' 3 'Michael' 4`"
3
}

Und der Code:

1
function displayScore () {
2
	if outOfRegularScore $2 $4 ; then

3
		checkEquality $2 $4
4
		checkAdvantage $1 $2 $3 $4
5
	else

6
		echo "$1: `convertToTennisScore $2` - $3: `convertToTennisScore $4`"
7
	fi
8
}
9
10
function checkAdvantage () {
11
	if [ $2 -gt $4 ]; then

12
		echo "$1: Advantage"
13
	elif [ $4 -gt $2 ]; then

14
		echo "$3: Advantage"
15
	fi
16
}

Dies funktioniert, aber die Funktion checkAdvantage ist doppelt vorhanden. Vereinfachen wir es und nennen es zweimal:

1
function displayScore () {
2
	if outOfRegularScore $2 $4 ; then

3
		checkEquality $2 $4
4
		checkAdvantage $1 $2 $4
5
		checkAdvantage $3 $4 $2
6
	else

7
		echo "$1: `convertToTennisScore $2` - $3: `convertToTennisScore $4`"
8
	fi
9
}
10
11
function checkAdvantage () {
12
	if [ $2 -gt $3 ]; then

13
		echo "$1: Advantage"
14
	fi
15
}

Dies ist tatsächlich besser als unsere vorherige Lösung und greift auf die ursprüngliche Implementierung dieser Methode zurück. Aber jetzt haben wir ein anderes Problem: Ich fühle mich mit den Variablen $1, $2, $3 und $4 unwohl. Sie brauchen aussagekräftige Namen:

1
function displayScore () {
2
	firstPlayerName=$1; firstPlayerScore=$2
3
	secondPlayerName=$3; secondPlayerScore=$4
4
5
	if outOfRegularScore $firstPlayerScore $secondPlayerScore; then

6
		checkEquality $firstPlayerScore $secondPlayerScore
7
		checkAdvantageFor $firstPlayerName $firstPlayerScore $secondPlayerScore
8
		checkAdvantageFor $secondPlayerName $secondPlayerScore $firstPlayerScore
9
	else

10
		echo "$1: `convertToTennisScore $2` - $3: `convertToTennisScore $4`"
11
	fi
12
}
13
14
function checkAdvantageFor () {
15
	if [ $2 -gt $3 ]; then

16
		echo "$1: Advantage"
17
	fi
18
}

Dies verlängert unseren Code, ist jedoch wesentlich aussagekräftiger. Ich mag das.

Es ist Zeit, einen Gewinner zu finden:

1
function testItCanOutputWinnerForFirstPlayer () {
2
	assertEquals 'John: Winner' "`displayScore 'John' 5 'Michael' 3`"
3
}

Wir müssen nur die Funktion checkAdvantageFor ändern:

1
function checkAdvantageFor () {
2
	if [ $2 -gt $3 ]; then

3
		if [ `expr $2 - $3` -gt 1 ]; then

4
			echo "$1: Winner"
5
		else

6
			echo "$1: Advantage"
7
		fi

8
	fi
9
}

Wir sind fast fertig! Als letzten Schritt schreiben wir den Code in tennisGame.sh, damit der Abnahmetest bestanden wird. Dies wird ein ziemlich einfacher Code sein:

1
#! /usr/bin/env sh

2
3
### tennisGame.sh ###

4
5
. ./functions.sh
6
7
playersLine=`head -n 1 $1`
8
echo "$playersLine"
9
firstPlayer=`getFirstPlayerFrom "$playersLine"`
10
secondPlayer=`getSecondPlayerFrom "$playersLine"`
11
12
wholeScoreFileContent=`cat $1`
13
totalNoOfLines=`echo "$wholeScoreFileContent" | wc -l`
14
for currentLine in `seq 2 $totalNoOfLines`
15
	do

16
	firstPlayerScore=$(getScoreFor $firstPlayer "`echo \"$wholeScoreFileContent\" | head -n $currentLine`")
17
	secondPlayerScore=$(getScoreFor $secondPlayer "`echo \"$wholeScoreFileContent\" | head -n $currentLine`")
18
	displayScore $firstPlayer $firstPlayerScore $secondPlayer $secondPlayerScore
19
done

Wir lesen die erste Zeile, um die Namen der beiden Spieler abzurufen, und lesen dann schrittweise die Datei, um die Punktzahl zu berechnen.


Abschließende Gedanken

Shell-Skripte können leicht von einigen Codezeilen auf einige hundert Zeilen erweitert werden. In diesem Fall wird die Wartung immer schwieriger. Die Verwendung von TDD und Komponententests kann die Wartung Ihres komplexen Skripts erheblich vereinfachen - ganz zu schweigen davon, dass Sie gezwungen sind, Ihre komplexen Skripte professioneller zu erstellen.