[Erledigt] Freetz: Wildcards im Script funktionieren nicht

  • Ersteller VirtuellOriginell
  • Erstellt am
V

VirtuellOriginell

Guest
Tach Gemeinde,
schreibe ein Script welches überprüft ob die eingegebene Uhrzeit das korrekte Format hat oder nicht.
Auf dem Linux Desktop funktioniert es damit

if [[ "$1" == [0-2][0-9]":"[0-9][0-9] ]]

Unter Freetz leider nicht :(


Hat jemand ne Idee wo mein Denkfehler liegt?
 
10:93 Uhr sähe verblüffend aus imo?
LG
 
"ash" ist keine "bash". Oder funktioniert das auf dem Linux-Desktop auch in der "ash" der BusyBox?

Wer solche Sachen POSIX-freundlich gestalten will, verwendet das "expr"-Utility (aber auch nur mit der POSIX-Syntax, es gibt BSD-Varianten mit abweichenden Parametern).

Dabei versucht man einfach ein (capturing) Match mit einem regulären Ausdruck (einfache Syntax, nicht "extended") und prüft das Ergebnis ... ist es "leer", paßte der Ausdruck nicht auf den gegebenen Wert.

Beispiel: https://github.com/PeterPawn/YourFritz/blob/master/scriptlib/functions/yf_base64_decode.function#L39 - hier wird getestet, ob die Zeile tatsächlich nur aus den Zeichen besteht, die im Base64-Vorrat erlaubt sind.
 
Auf dem Linux Desktop funktioniert es damit

if [[ "$1" == [0-2][0-9]":"[0-9][0-9] ]]

der regex-Ausdruck sieht sehr "unglücklich" aus;
Verbesserungsvorschlag:
# echo "23:41" | egrep '([01][0-9]|2[0-3]):[0-5][0-9]'
23:41
# echo "29:41" | egrep '([01][0-9]|2[0-3]):[0-5][0-9]'
#
diese Befehlssequenz mit "echo "$1" ... " sollte mit Pipe to "wc -l" in Verbindung mit "-eq 1" auch als if-Abfrage umsetzbar sein.
 
Für die busybox ash und egrep mal flux eine Funktion getestet...
Code:
# test(){ if [ $(echo $1 | egrep '([01][0-9]|2[0-3]):[0-5][0-9]') ]; then echo ok; else echo fail; fi }
# test 99:99
fail
# test 23:59
ok
# test 00:00
ok
 
Einen solchen Test auf "null string" als Inhalt von STDOUT einer Sub-Shell sieht man nicht soo häufig und selbst viele, die schon mal ein Shell-Skript geschrieben haben, müssen diese Syntax-Form nicht zwingend kennen.

Und wehe, diese Ausgabe enthält dann mehr als einen Wert (auch wenn das in #5 wegen des Ausdrucks nicht möglich ist (wenn man den Eingabewert vorher schon getestet hat, komme ich gleich noch einmal drauf zurück), bleibt es beim "Adaptieren" eine Falle, so man den Unterschied nicht kennt):
Code:
vidar:/tmp # test $(echo) && printf "true" || printf "false";printf "\n"
false
vidar:/tmp # test $(echo 1) && printf "true" || printf "false";printf "\n"
true
vidar:/tmp # test $(echo 1 2) && printf "true" || printf "false";printf "\n"
bash: test: 1: unary operator expected
false
vidar:/tmp # test "$(echo 1 2)" && printf "true" || printf "false";printf "\n"
true
vidar:/tmp # [ $(echo 1 2) ] && printf "true" || printf "false";printf "\n"
bash: [: 1: unary operator expected
false
vidar:/tmp #
Daher mein Tipp: Wenn man "die Wahl" hat, sollte man es (meine Meinung) dem "Leser" einfacher machen und tatsächlich einen Vergleich benutzen und sich nicht (erst recht nicht bei der Form von "test" mit den eckigen Klammern) auf die "test <string>"-Syntax verlassen und man sollte auch immer damit rechnen, daß ja mal ein "unerwarteter" Wert auftauchen könnte (hier für "$1" in der "test"-Funktion) und daher prinzipiell alle Zeichenketten (und die STDOUT einer Sub-Shell gehört definitiv dazu, denn was passiert denn beim Aufruf von
Code:
vidar:/tmp # test(){ if [ $(echo $1 | egrep '([01][0-9]|2[0-3]):[0-5][0-9]') ]; then echo ok; else echo fail; fi }
vidar:/tmp # test "01:00 02:00"
bash: [: 01:00: unary operator expected
fail
vidar:/tmp #
) passend ein- bzw. umschließen, dann kann das auch keine bösen Überraschungen geben:
Code:
vidar:/tmp # test(){ if [ "$(echo $1 | egrep '([01][0-9]|2[0-3]):[0-5][0-9]')" ]; then echo ok; else echo fail; fi }
vidar:/tmp # test "01:00 02:00"
ok
vidar:/tmp #
... ob das dann tatsächlich noch "ok" ist oder nicht, muß jeder selbst entscheiden.

Denn so ein "egrep" liefert eben auch dann noch ein Ergebnis, wenn das nur irgendwo in der Zeile steht:
Code:
vidar:/tmp # echo "01:00" | egrep '([01][0-9]|2[0-3]):[0-5][0-9]'
01:00
vidar:/tmp # echo "01:00 02:00" | egrep '([01][0-9]|2[0-3]):[0-5][0-9]'
01:00 02:00
vidar:/tmp # echo "01:00 02:00 ABCDEFGH" | egrep '([01][0-9]|2[0-3]):[0-5][0-9]'
01:00 02:00 ABCDEFGH
... also sollte man noch zusätzlich die Forderung aufstellen, daß die gesamte Zeile auf das Muster paßt:
Code:
vidar:/tmp # echo "01:00 02:00 ABCDEFGH" | egrep '^([01][0-9]|2[0-3]):[0-5][0-9]$'
vidar:/tmp # echo "01:00" | egrep '^([01][0-9]|2[0-3]):[0-5][0-9]$'
01:00
vidar:/tmp #

Fazit:
Manchmal ist es tatsächlich eine Herausforderung, Shell-Code so zu formulieren, daß er auch wirklich "idiotensicher" ist und sich auch von unerwarteten Eingabewerten nicht wirklich aus der Ruhe bringen läßt. :D
 
"ash" ist keine "bash". Oder funktioniert das auf dem Linux-Desktop auch in der "ash" der BusyBox?
Ja auf dem Linux läuft 'ne Bash-shell, und eigentlich sollte auch Freetz auch Bash können, ist im Image drin.

Edit hab etwas rum gespielt, dies scheint zu funktionieren.
Code:
if [ $(echo $1 |egrep '^([0-2][0-9]):[0-5][0-9]$' |wc -c) -gt 1 ]
   then
   echo $1 > zeitstempel.txt
   
elif [ $(echo $1 |egrep '^([0-9]):[0-5][0-9]$' |wc -c) -gt 1 ]
   then
   echo "0"$1 > zeitstempel.txt
   
elif [ $(echo $1 |egrep '^([012][0-9]):[0-5]$' |wc -c) -gt 1 ]
   then
   echo $1"0" > zeitstempel.txt
   
elif [ $(echo $1 |egrep '^([0-9]):[0-5]$' |wc -c) -gt 1 ]
   then
   echo "0"$1"0" > zeitstempel.txt

else
   echo "Bärenjagt"
   echo $(date +%H:%M) > zeitstempel.txt
fi

cat zeitstempel.txt


until [ "$(date +%R)" == "$(head -n1 zeitstempel.txt)" ]; do
   sleep 1
   echo $(date +%T) "Weißer Hase" $(tail -n1 zeitstempel.txt)
done

echo $(date +%T) "Jäger mit dem Schießgewehr"
rm zeitstempel.txt

Was ich aber nicht verstehe wozu brauch ich die () runden Klammern vor den eckigen Klammern? Ist das wegen des Doppelpunktes?
 
Zuletzt bearbeitet von einem Moderator:
I mache das immer so
Funktioniert immer, auch mit busybox

Code:
if echo $1 | grep -q '^[0-2][0-9]:[0-9][0-9]$' ; then

oder

Code:
echo $1 | grep -q '^[0-2][0-9]:[0-9][0-9]$' || echo "Nicht gut"
 
Zuletzt bearbeitet:
@frater:
Das hängt sicherlich auch davon ab, ob man "29:99" für "gut" oder "nicht gut" hält.

Es gibt immer viele Möglichkeiten in Shell-Code, aber ich würde immer zu etwas wie diesem greifen, weil das tatsächlich überall funktionieren sollte, wo POSIX verstanden wird - von der "bash" über die "dash" bis zur "ash" in der BusyBox:
Code:
vidar:~ $ validate_time() { [ "$(expr \( "$1" : '^\(\([0-1 ]\?[0-9]\|2[0-3]\):[0-5][0-9]\)$' \) )" = "$1" ] && printf "ok\n" || printf "wrong time specified\n"; }
vidar:~ $ validate_time "0:00"
ok
vidar:~ $ validate_time "10:00"
ok
vidar:~ $ validate_time " 0:00"
ok
vidar:~ $ validate_time "0 :00"
wrong time specified
vidar:~ $ validate_time "23:59"
ok
vidar:~ $ validate_time "24:00"
wrong time specified
vidar:~ $
Das sollte eigentlich jede gültige Zeitangabe (24-h-Format) abdecken, von zweistelligen Stundenangaben bis zu einstelligen, mit oder ohne Leerzeichen vor der Ziffer bei einstelligem Wert und das alles ohne "wc"-Geraffel.

Man kann das natürlich auch gleich ohne den Parameter ($1) einsetzen und bei "ok" einfach den Wert in die Datei schreiben, ansonsten eben den Ersatz wie oben im "else".

Wenn ich mich nicht vertan habe, läßt sich das auch nicht durch unerwartete Eingabewerte aus dem Tritt bringen.

EDIT:
Wenn man will, kann man auch gleich noch die Werte "umformatieren" lassen, so daß sie immer zweistellig sind:
Code:
vidar:~ $ validate_time() { [ "$(expr \( "$1" : '^\(\([0-1 ]\?[0-9]\|2[0-3]\):[0-5][0-9]\)$' \) )" = "$1" ] && printf "%02u:%02u\n" $(( ${1%%:*} )) $(( ${1##*:} )) || printf "wrong time specified\n"; }
vidar:~ $ validate_time "9:16"
09:16
vidar:~ $
und auch der Fall mit einer einstelligen Minutenangabe (eher exotisch, etwas wie "11:8 Uhr" sieht man nur sehr selten und der dritte und vierte Fall in #7 sollten ohnehin falsch sein, wenn man "11:5 Uhr" nicht als "11:50 Uhr" ansehen will, was ich nun noch merkwürdiger fände) ist mit einem "\?" nach dem "[0-5]" im regulären Ausdruck noch eingeschlossen:
Code:
vidar:~ $ validate_time() { [ "$(expr \( "$1" : '^\(\([0-1 ]\?[0-9]\|2[0-3]\):[0-5]\?[0-9]\)$' \) )" = "$1" ] && printf "%02u:%02u\n" $(( ${1%%:*} )) $(( ${1##*:} )) || printf "wrong time specified\n"; }
vidar:~ $ validate_time "9:6"
09:06
vidar:~ $

EDIT2: Da war noch eine Gruppierung zuviel bei der Stunde (nicht falsch, aber unnötig), die habe ich entfernt.
 
Zuletzt bearbeitet:
Arbeitest Du immer als root? Ganz schön gefährliches Beispiel...
 
Besser jetzt? Die Raute muß auch nicht automatisch "root" heißen.

Der Prompt ist ohnehin - wie meist die gesamte Ausgabe - editiert (früher gab's die Farben zur Unterscheidung zwischen Ein- und Ausgabe auch noch in den CODE-Kästen zu sehen) ... da ich bei solchen Sachen auch nicht immer wirklich im eigenen Home-Verzeichnis arbeite (bzw. auch die Tilde für $HOME normalerweise bei mir gar nicht angezeigt wird, sondern der absolute Pfad), verschleiere ich auf diesem Weg auch den Benutzernamen (denke ich fast immer dran) und da kann es auch mal passieren, daß ich eine Raute eintrage bzw. nicht "richtig" ersetze.

Denn normalerweise habe ich sogar folgendes (sinngemäß und schon stark zusammengekürzt, hoffentlich immer noch richtig) in der ".profile" stehen - noch schön bunt mit Escape-Sequenzen, aber umschaltbar anhand des Terminal-Typs und mit besonderer Farbe, falls ich tatsächlich mal "root" bin, was bei einem GitHub-Repository für die FRITZ!Box gar nicht sooo verkehrt ist, weil dann UID/GID beim Klonen und in TAR-Files, die aus diesen Dateien gepackt werden, schon stimmen:
Code:
export PROMPT_COMMAND='LASTRC=$?;echo -en "\n$LASTRC | $(hostname -s):$USER:$PWD | ${SSH_TTY:-\$}${SSH_TTY:+#}\007 "'
export PS1=
... das ergibt dann z.B.:
Code:
0 | vidar:peh:/home/GitHub/YourFritz/eva_tools | /dev/pts/0#

Warum mache ich das?
  • weil ich den "normalen" Prompt mit dem Dollar nicht mag, wenn ich per SSH unterwegs bin (was auch fast immer der Fall ist) und eine "screen"-Session benutze, so sehe ich auch immer, ob es SSH ist oder nicht und soo groß ist die Auswahl an (unmißverständlichen) Prompt-Sonderzeichen nun auch wieder nicht, daß ich die Raute für "root" reservieren möchte (die Farbe ist ohnehin eindeutiger)
  • und weil ich dann mit Suchen und Ersetzen arbeiten kann beim Editieren von kopiertem Terminal-Inhalt für solche CODE-Kästen, was bei Dollarzeichen (gerade bei Shell-Code) und beim Editieren im "vi" immer passende Escapes braucht und ziemlich nervig sein kann (mein Browser kann das mit dem Suchen und Ersetzen(!) beim Schreiben eines Beitrags jedenfalls nicht und ich muß das immer außerhalb aufbereiten)
  • und weil ich - gerade bei länger laufenden Kommandos - das "bell" als akustisches Zeichen für "fertig" haben möchte (dann blinkt auch die Session im "screen" immer so nett in der Statuszeile, falls es mal nicht quietscht) - dieses "beep" gibt es allerdings auch ansonsten ständig, wenn eine "tab completion" noch nicht eindeutig ist, das kriege ich (leider) schon meist gar nicht mehr richtig mit
  • und weil ich gleichzeitig gerne den letzten Exit-Code wissen möchte (alte Angewohnheit aus meiner Großrechner-Vergangenheit und aus SCO-Tagen) - sogar noch in bunt beim passenden Terminal, damit man <> 0 auf einen Blick erkennen kann - häufig habe ich mehr als eine Session (sowohl im SSH-Client als auch im "screen" in so einer SSH-Session) offen und kehre erst ins Terminal zurück, wenn ein Kommando beendet ist
Am Ende hat bei mir die "bash" fast mehr zu tun, wenn sie den Prompt anzeigen soll, als wenn ich irgendein Kommandos ausführen lasse und meist kommt meine Eingabe auch erst auf die nächste Zeile, weil da (sofern es keine lokale Session im LAN ist) noch die Angabe zur SSH-Connection hinzukommt. Dann kommt (abhängig von der Länge des Prompts) gleich ein "\n" ans Ende anstelle des Leerzeichens ... das ist dann auch eine gute Lösung und man kriegt die Eingabe (auch beim Editieren vorhergehender Kommandos) noch auf eine einzelne Terminal-Zeile in der Regel, was ansonsten schnell schwierig wird.
 
Schon gut! Wollt nur drauf hinweisen wegen der Anfänger hier, weil Du ja immer ganze Vorlesungen hältst :D
 
Schon gut ... die erkennen dann aber auch nicht unbedingt, daß "üblicherweise" die Raute das "\$" im PS1 für den Superuser ist.
 
@frater:
Das hängt sicherlich auch davon ab, ob man "29:99" für "gut" oder "nicht gut" hält.

;)

Die regexp selbst habe ich nicht kontrolliert.
Das war doch nicht das Thema?

Ich meine die weise wie man das schreibt.
Es ist viel einfacher zu lesen und funktioniert in (glaube ich) alle shell varianten.

Schön das man mit Bash inline schöne Sachen machen kann, aber es sieht schnell ganz kryptisch aus und auch die viele Klammern machen es auch nicht schöner.
 
Warum ändert du dann die Sequenz nicht wenigstens so ab, wie es @koyaanisqatsi in Beitrag #5 beschrieben hat?
 
Es ist viel einfacher zu lesen und funktioniert in (glaube ich) alle shell varianten.
Wenn man Kopfschmerzen hinsichtlich der "Lesbarkeit" hat, sollte man einen Kommentar dazuschreiben und erklären, was da nach eigener Ansicht passieren soll.

So, wie Du es vorschlägst, sieht es zwar "ganz einfach" aus, kann aber genauso einfach auch zur Fehlfunktion gebracht werden. Probier das mal mit
Code:
$1="--"
... und daß ein Ersetzen eines einzelnen (korrekten) regulären Ausdrucks durch vier verschiedene - "damit die Komplexität nicht so hoch ist" - nicht zwangsläufig besser und richtiger ist, siehst Du in #7, wenn Du genau nachliest.

Ich bin auch für einfache und klare Lösungen und finde komplizierte "if-then-else"-Abfragen (möglichst noch mit Negation bei einem Vergleich und diversen Klammern für den Vorrang (die braucht es nun mal häufig, um eine zweifelsfreie Reihenfolge der Auswertung zu garantieren) und diversen Umformungen nach Boolescher Algebra) am Ende viel schlimmer, als einen solchen regulären Ausdruck, den man genau ein einziges Mal analysiert und dann sofort (einfaches "if-then-else", ohne jede Schachtelung) sehen kann, was beim Erfolg oder Mißerfolg geschehen soll.

Komplexität bringen erst die erwähnten Konstruktionen in irgendwelchen Code ... da sind die vier "Zweige" in #7 ein schönes Beispiel - wobei hier wenigstens noch die "Rahmenbedingungen" um das "egrep" dieselben sind in allen vier Zweigen (und man sich damit bei der Analyse auf die Auswertung des regulären Ausdrucks (hier dann eben 4x) beschränken kann, wobei man die "unterschiedlichen Reaktionen" auch noch ansehen und verstehen muß), was auch nicht selbstverständlich so sein muß.
 
Warum ändert du dann die Sequenz nicht wenigstens so ab, wie es @koyaanisqatsi in Beitrag #5 beschrieben hat?
Für die busybox ash und egrep mal flux eine Funktion getestet...
Code:
# test(){ if [ $(echo $1 | egrep '([01][0-9]|2[0-3]):[0-5][0-9]') ]; then echo ok; else echo fail; fi }
# test 99:99
fail
# test 23:59
ok
# test 00

:00
ok

So wurde ich koyaanisqatsi's "if statement" schreiben und auf dieser weise mach ich das immer in Bash scripts.
Nur ein wenig anders.
Liest sich auch besser (nach meiner Meinung)

Es geht darum das "[ $( .... ) ]" uberflussig ist und dass die doppelquote notwendig ist.

Code:
#
# timecheck(){ if echo "$1" | egrep -q '([01][0-9]|2[0-3]):[0-5][0-9]' ; then echo ok; else echo fail; fi }
# timecheck 99:99
fail
# timecheck 23:59
23:59
ok
# timecheck 00:00
00:00
ok

@PeterPawn In mein eigene scripts vergesse ich niemahls die doppel-quotes. Ist das was du meinst?
Kein Fehlfunktion die ich sehe.

So, wie Du es vorschlägst, sieht es zwar "ganz einfach" aus, kann aber genauso einfach auch zur Fehlfunktion gebracht werden. Probier das mal mit
Code:
$1="--"

Code:
# timecheck --:00
fail
# timecheck --
fail
 
Zuletzt bearbeitet:
Sorry, ich hatte mich da leicht vertan!
Ich meinte eigentlich den Vorschlag von @tuxedonet aus Beitrag #4.
 
@frater:
Ich weiß langsam nicht mehr, worauf Du hinauswillst.

Ja, für mich gehören immer "quotes" um eine Zeichenkette, denn das sind zwei vollkommen unterschiedliche Ergebnisse:
Code:
vidar:~ $ a="-e \007"
vidar:~ $ echo $a

vidar:~ $ echo "$a"
-e \007
vidar:~ $

Und daß Dein Code in #17:
Code:
# timecheck(){ if echo "$1" | egrep -q '([01][0-9]|2[0-3]):[0-5][0-9]' ; then echo ok; else echo fail; fi }
auch bei falscher Eingabe nicht wirklich etwas merkt, dürfte offensichtlich sein:
Code:
vidar:~ $ timecheck(){ if echo "$1" | egrep -q '([01][0-9]|2[0-3]):[0-5][0-9]' ; then echo ok; else echo fail; fi }
vidar:~ $ timecheck "01:23 Ich bin mit ziemlicher Sicherheit keine gültige Uhrzeit."
ok
vidar:~ $ timecheck "Auch dann nicht, wenn die Zeit irgendwo - z.B. hier: 01:23 - in der Mitte steht."
ok
vidar:~ $
 
@frater:
Ich weiß langsam nicht mehr, worauf Du hinauswillst.
Es war mir niemahls um die regexp zu tun.
Die OP hat dieser if aufbau genommen:
Code:
if [[ "$1" == [0-2][0-9]":"[0-9][0-9] ]] ; then

Später hat jemand:
Code:
if [ $(echo $1 |egrep '^([0-2][0-9]):[0-5][0-9]$' |wc -c) -gt 1 ] ; then



Das einzige was ich teilen wollte war das as auch so kann:
Code:
if  echo "$1" | egrep -q "^${regexp}$" ; then

Und es stimmt das ich nicht alle Eintragen gut gelesen habe, aber immer noch ist es ein Beitrag..
 

Neueste Beiträge

Statistik des Forums

Themen
244,999
Beiträge
2,222,439
Mitglieder
371,773
Neuestes Mitglied
silverstar_ms
Holen Sie sich 3CX - völlig kostenlos!
Verbinden Sie Ihr Team und Ihre Kunden Telefonie Livechat Videokonferenzen

Gehostet oder selbst-verwaltet. Für bis zu 10 Nutzer dauerhaft kostenlos. Keine Kreditkartendetails erforderlich. Ohne Risiko testen.

3CX
Für diese E-Mail-Adresse besteht bereits ein 3CX-Konto. Sie werden zum Kundenportal weitergeleitet, wo Sie sich anmelden oder Ihr Passwort zurücksetzen können, falls Sie dieses vergessen haben.