PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Bash: Wie lösche ich am elegantesten eine Zeile?



killi_pilli
27-01-2008, 15:01
Hallo zusammen!

Bei folgendem Script würde ich gerne insbesondere die Zeile, die "Wartezeit beendet" ausgibt, etwas eleganter hinkriegen. Wisst ihr Lösungen?


#!/bin/bash

while [ 1 ] # Endlos-Schleife, die erst bei korrekter Eingabe mittels exit beendet wird (siehe weiter unten)
do
read -p "Wieviele Sekunden warten? " # Abfrage mit Option -p für den angezeigten Text

if [ -z $REPLY ] # prüft ob überhaupt etwas eingegeben wurde ($REPLY ist die Eingabe)

then # nichts eingegeben
echo "Eingabe ist leer. Nochmal..."

else
if [[ `echo "$REPLY" | grep -E ^[[:digit:]]+$` ]] # prüft ob die Eingabe eine Zahl (digit) ist

then
while [ $REPLY -ge 2 ] # zählt runter bis auf 2 (wegen Ausgabe: Sekunden)
do
echo -en "\rWarte noch $REPLY Sekunden..." # schreibt immer wieder in die selbe Zeile
sleep 1 # 1 Sekunde warten
REPLY=$[$REPLY-1] # Zähler um 1 verringern
done
echo -en "\rWarte noch 1 Sekunde..." # Ausgabe für die letzte Sekunde wegen der Grammatik ;-)
sleep 1
echo -en "\rWartezeit beendet! " # Leerzeichen löschen übrig gebliebene Zeichen am Ende der Zeile
echo # mit Cursor in die nächste Zeile springen und Script beenden
exit

else # wenn Eingabe keine Zahl ist
echo "$REPLY ist ungültig. Es gehen nur ganze Zahlen. Nochmal..."
fi
fi
done

jan61
27-01-2008, 22:14
Moin,

ich weiss zwar nicht, was Dir an der Zeile nicht gefällt, aber wenn Du Möglichkeiten suchst, auf dem Terminal rumzuhopsen und zu malen, dann schau Dir mal tput an. Ist aber für das kleine Script IMHO oversized.

Jan

jan61
28-01-2008, 23:46
Moin,

ich habe mir Dein Script heute nochmal genauer angesehen, da sind mir ein paar Stellen aufgefallen, bei denen Du Dir - ähm, sozusagen schlechte Angwohnheiten zu eigen gemacht hast, die Dir in anderen Fällen Schwierigkeiten einbringen können.

Zeile 7: if [ -z $REPLY ]
Das klappt zum einen nur mit dem GNU-test, andere Unixe hauen Dir einen Syntax-Error um die Ohren, weil eben in dem Fall, dass $REPLY leer ist, hinter -z kein Argument steht. Und wenn Du dann versuchst, mit dem -n-Operator Gleiches zu treiben, kriegst Du ein eher unerwartetes Ergebnis. Besser ist es immer, die Variable zu quoten (die Kommentare habe ich nachträglich eingefügt):
jan@jack:~/tmp> a=
jan@jack:~/tmp> if [ -z $a ]; then echo leer; fi
leer # ok
jan@jack:~/tmp> if [ -n $a ]; then echo nicht leer; fi
nicht leer # ups?
jan@jack:~/tmp> if [ -z "$a" ]; then echo leer; fi
leer # ok
jan@jack:~/tmp> if [ -n "$a" ]; then echo nicht leer; fi
jan@jack:~/tmp># auch okUnd selbst die bash und der GNU-Test sind nicht grenzenlos tolerant. Das merkst Du dann, wenn Du weitere Bedingungen einbaust:
jan@jack:~/tmp> a=
jan@jack:~/tmp> if [ -z $a -a 1 -eq 1 ]; then echo leer; fi
bash: [: too many arguments
jan@jack:~/tmp> if [ -z "$a" -a 1 -eq 1 ]; then echo leer; fi
leer
2. Zeile 13: if [[ `echo "$REPLY" | grep -E ^[[:digit:]]+$` ]]
Erste Frage: Wer zum Geier hat denn diese Doppel-Tests erfunden ([[ ... ]]) - was bringen die denn außer Verwirrung, Chaos und Syntax-Fehlern (siehe unten)?
jan@jack:~/tmp> a=
jan@jack:~/tmp> if [[ `echo "$a" | grep -E ^[[:digit:]]+$` ]]; then echo numerisch; fi
jan@jack:~/tmp> if [ `echo "$a" | grep -E ^[[:digit:]]+$` ]; then echo numerisch; fi
jan@jack:~/tmp> a=42
jan@jack:~/tmp> if [[ `echo "$a" | grep -E ^[[:digit:]]+$` ]]; then echo numerisch; fi
numerisch
jan@jack:~/tmp> if [ `echo "$a" | grep -E ^[[:digit:]]+$` ]; then echo numerisch; fi
numerisch
Das zweite Problem: Ein regulärer Ausdruck im grep (wie in allen anderen Befehlen, die sowas verarbeiten) sollte gequotet werden - Dein grep geht nur zufällig gut, weil die regex nicht durch die Shell ausgewertet werden kann. Sonst gibt es nämlich wieder angefangen bei unerwarteten Ergebnissen bis hin zu Syntax-Fehlern alles, was das Programmiererherz sich wünscht:
jan@jack:~/tmp> a=a
jan@jack:~/tmp> if [ `echo "$a" | grep -E a*` ]; then echo enthaelt a; fi
bash: [: too many arguments
Warum der Fehler? Die Shell wertet das (zufälligerweise in regex auch sehr gebräuchliche) * vor dem Aufruf des grep aus und ersetzt es durch eine Liste aller Dateien im aktuellen Verzeichnis, die mit a anfangen. Der Name der ersten Datei wird dann zum Suchmuster (zufälligerweise gibts bei mir eine Datei namens "a") und nach diesem Suchmuster wird dann gesucht - und zwar in allen folgenden Dateien, die mit a anfangen. Besser so:
jan@jack:~/tmp> if [ `echo "$a" | grep -E 'a*'` ]; then echo enthaelt a; fi
enthaelt a
Noch schlimmer wird die Sache, wenn wie hier die erste Datei einen Namen hat, der in keiner der anderen Dateien auftaucht - beliebig fiese Konstellationen sind denkbar:
jan@jack:~/tmp/elchtest/1> ls -ls
insgesamt 4
4 drwxr-xr-x 2 jan users 4096 2008-01-28 23:59 a
0 -rw-r--r-- 1 jan users 0 2008-01-28 23:51 abc
0 -rw-r--r-- 1 jan users 0 2008-01-28 23:54 abcde
0 -rw-r--r-- 1 jan users 0 2008-01-28 23:58 abc.jpg
0 -rw-r--r-- 1 jan users 0 2008-01-28 23:51 cba
jan@jack:~/tmp/elchtest/1> ls a
jan@jack:~/tmp/elchtest/1> a=a
jan@jack:~/tmp/elchtest/1> if [ `echo "$a" | grep -E a*` ]; then echo enthaelt a; fi
jan@jack:~/tmp/elchtest/1># offensichtlich falsch aber logisch: keine der DateiINHALTE enthält ein "a", sie sind ja leer
Dritter Meckerpunkt: Wenn ich die Ausgabe eines Kommandos auswerte und nicht definitiv weiß, dass da genau ein Wert zurückkommt, dann quote ich die Ausgabe und setze einen Operator davor (siehe Kritik zu Zeile 7):
if [ -n "`echo \"$a\" | grep -E 'a*'`" ]; then echo enthaelt a; fiSo, und jetzt zu diesem komischen [[..]]-Geraffel: Damit kriegst Du ein paar der o. g. Fälle geregelt, aber es knallt endgültig, wenn Du noch was anderes prüfen willst:
jan@jack:~/tmp/elchtest/1> if [[ `echo "$a" | grep -E a*` -a 1 -eq 1 ]]; then echo enthaelt a; fi
bash: conditional binary operator expected
bash: syntax error near `-a'
So isses besser - und richtig:
jan@jack:~/tmp/elchtest/1> a=a
jan@jack:~/tmp/elchtest/1> if [ -n "`echo \"$a\" | grep -E 'a.*'`" -a 1 -eq 1 ]; then echo enthaelt a; fi
enthaelt a
jan@jack:~/tmp/elchtest/1> a=b
jan@jack:~/tmp/elchtest/1> if [ -n "`echo \"$a\" | grep -E 'a.*'`" -a 1 -eq 1 ]; then echo enthaelt a; fi
jan@jack:~/tmp/elchtest/1>Jan