Fallgruben in BASH
Posted on July 10th, 2014 in Konsole, Programmierung, Ubuntu | von Bernhard Essl | 9 Comments »
Immer wieder bin ich angenehm überrascht wie viele Aufgaben ich mit BASH in oft einer (langen) oder nur wenigen Zeilen lösen kann. Warum schreibe ich dann fast alles in Skriptsprachen wie Ruby, Python oder Perl?
Ganz einfach weil die Syntax einfacher zu merken ist und BASH doch einige Eigenheiten hat, die es zu merken gilt.
Darum hier ein Versuch sämtliche gern gemachte Fehler niederzuschreiben. Ich würde mich über Kommentare eurer Fallgruben von euch freuen.
- whitespaces und wildcards
mv $file_old $file_new # falsch
Das kann bei Dateinamen mit Leerzeichen oder wildcards zu Problemen führen, wenn man sich nicht sicher ist welchen Inhalt eine Variable hat, sollte man sie in Anführungszeichen setzen.
mv "$file_old" "$file_new"
Nun können in $file_old und $file_new Leerzeichen und wildcards wie * oder ? oder [...] verwendet werden.
- runde Klammern werden nicht erkannt
Ich bin schon öfter über Posting gestolpert wo das Skript mit dieser Meldung abstürzte:
Syntax error: "(" unexpected
Das liegt dann meistens daran das keine BASH sondern eine abgemagerte Shell wie dash verwendet wurde.
Welche Shell verwendet wird kann über $SHELL abgefragt werden:echo $SHELL
- Variablen-Deklaration
$ubuntu="super" # falsch ubuntu = "super" # falsch ubuntu ="super" # falsch
in BASH werden einer Variable, ohne $ und ohne Zwischenabstände Werte zugewiesen.
ubuntu="super"
- lesen und schreiben in die selbe Datei
Es ist nicht möglich in die selbe Datei, über eine pipe zu schreiben, von der gelesen wird. In den meisten Fällen bleibt dann eine 0 Byte große Datei über.
cat datei | sed s/foo/bar/ > datei # falsch
Eine Lösung wäre mit einer temporären Datei zuarbeiten und diese mit der Originaldatei zu überschreiben
sed 's/foo/bar/g' datei > tmpdatei && mv tmpdatei datei
- Globing verwenden
Ein gern gemachter Fehler ist das verwenden von subshells in loops
for x in `ls *.jpg`; do # falsch
Hat einer der Dateinamen whitespaces wird es hier zu Fehlern kommen (siehe 1). Außerdem wird ein externes Kommando ls unnötig aufgerufen.
In BASH sollte darum für solche Aufgaben das BASH interne Glob verwendet werden.for i in *.jpg; do
- Verwenden absoluter Pfade bei externen Programmen
Oft werden externe Programme in BASH ohne absoluten Pfad aufgerufen, sehr sicher ist das allerdings nicht.
dateien=`ls /home/userin/`
Das Problem hier könnte sein das jemand mutwillig ein Programm mit den Namen ls erzeugt und nicht mehr /bin/ls wie gedacht, sondern ein manipuliertes ls ausgeführt wird.
Ein besserer Ansatz mit einem absoluten Pfad:ls="/bin/ls" dateien=`$ls /home/userin/`
9 Responses
Guten Morgen
Schöne Auflistung Zwei Anmerkungen: Punkt 4 ist meines Wissens nicht grundsätzlich falsch. In diesem speziellen Fall jedoch kann sed direkt die Datei bearbeiten, nämlich mit dem Schalter -i, der dem ‘s…’ vorangestellt werden könnte. Kann aber natürlich trotzdem Sinn machen, so vorzugehen, wenn man dann die temporäre Datei noch prüft.
Bei Punkt 6 stimme ich nicht zu. Der Gedanke ist nicht ganz verkehrt, aber das Nutzen von absoluten Pfaden führt dazu, dass das Programm scheitert, sobald die Dateistruktur nicht wie erwartet ist. Ubuntu hat den Ort der coreutils z.B. mal verändert. Darauf sollte man sich besser nicht verlassen.
Gruß
Zu 4. möchte ich anmerken, dass “sed -i” die einfachere Variante ist.
Die letzte Zeile sollte wohl lauten:
dateien=`$ls /home/userin/`
Zudem stellt eine im lokalen Verzeichnis befindliche Datei so lange kein Risiko dar, so lange “.” nicht im Suchpfad ist, was ja defaultmäßig bei allen Unix-Installationen, die ich kenne, nicht gemacht wird.
Eine lokales ls müsste man also mit ./ls aufrufen, sonst wird trotzdem /bin/ls ausgeführt.
Zu 2.: Ich habe echo shell eingegeben, und was wurde geantwortet? Richtig, shell.
Zu 4:
sed -i ist das was du suchst…:)
stimmt sed -i wäre richtig, mir ist nur kein besseres Beispiel eingefallen
@Andreas Abendroth $SHELL
@Gerald vollkommen richtig danke
@onli guter Punkt danke
Das Problem mit den Whitespaces bei Schleifen kann man auch anders umgehen:
ls | while read line; do something $line; done
Und anders als bei “for a in *; do …” kann man auch z.B. mit “find” ganze Verzeichnissbaeume behandeln.
[...] Fallgruben in BASH Ich liebe solche Posts! [...]
$SHELL abzufragen ist an sich recht sinnfrei, weils nur die umgebungsvariable abfragt und in der kann auch “räuber hotzenplotz” stehen. obwohl ich ne ksh verwende sagt er mir dann nämlich trotzdem “bash”:
-bash-3.00$ echo $SHELL
/bin/bash
-bash-3.00$ echo $0
-bash
-bash-3.00$ ksh
$ echo $SHELL
/bin/bash
$ echo $0
ksh