Script Shell - Wie druckt man ein Echo aus, wenn zwei Bedingungen erfüllt sind? (Nur Zahlen, genau 2 Bedingungen)

Script Shell - Wie druckt man ein Echo aus, wenn zwei Bedingungen erfüllt sind? (Nur Zahlen, genau 2 Bedingungen)

Ich bin neu im Shell-Scripting und versuche, die Grundlagen zu erlernen. Ich starte ein Skript über das Terminal, z. B. „./scriptgoeshere.sh Argument1 Argument2“.

Ich möchte, dass das Skript ein Echo ausgibt. „Hallo Welt“ ist wahrscheinlich das beste Echo, aber ich möchte, dass es „Hallo Welt“ nur ausgibt, wenn es genau zwei Argumente gibt und diese Argumente ausschließlich aus Zahlen bestehen müssen. Beispiel:

./scriptgoeshere.sh 1523 9643 ./scriptgoeshere.sh 7 96 Sollte funktionieren, jedoch; ./scriptgoeshere.sh 594 arg Sollte NICHT funktionieren, dies ist das Skript, das ich im Moment habe, aber es funktioniert nicht. Ich habe den ganzen Tag versucht, das herauszufinden, aber ich kriege es einfach nicht hin. Ich habe sogar if/elif/else ausprobiert, wo ich prüfen ließ, ob im if-Teil 2 Eingaben vorhanden sind und ob sie beide Zahlen in elif sind, bevor ich fortfahre.

#!/bin/bash
if [ "$#" -eq 2 ] && [ "$@" -eq ^[0-9] ]; 
then
    i=0
    while [ $i -lt 3 ]
    do
        echo "Hello World!"
        sleep 5
        i=$((i+1))
    done
else
echo "Need two arguments and only numbers will be accepted, such as ./scriptgoeshere 153 251"

fi

Es blockiert die Hallo-Welt-Schleife, bis ich zwei Argumente eingebe, aber es spielt keine Rolle, ob es sich um Zahlen oder Text handelt, und ich erhalte dann einen Fehler: „./scriptgoeshere.sh: Zeile 2: [: ^[0-9]: ganzzahliger Ausdruck erwartet“. Ich erhalte diesen Fehler auch, wenn ich ./scriptgoeshere.sh 123 123 in das Terminal schreibe, um es zu starten. Ich löse es lieber nicht mit Variablen, da ich die Argumente beim Starten des eigentlichen Skripts herausfinden möchte.

Das macht mich verrückt!

Damit habe ich angefangen, um zu prüfen, ob genau 2 Argumente vorhanden sind, und es funktioniert. Ich möchte nur eine kleine Korrektur hinzufügen, sodass diese 2 Argumente Zahlen sein MÜSSEN. Ich verstehe nicht, warum das so schwierig zu sein scheint.

#!/bin/bash
if [ "$#" -eq 2 ];
then
    i=0
    while [ $i -lt 3 ]
    do
        echo "Hello World!"
        sleep 1
        i=$((i+1))
    done
else
echo "Need two argument, only numbers accepted"

fi

Bearbeiten: Ich habe es gelöst. Ich war so nah dran. Ich musste nur noch ein zusätzliches [] zum Zahlenteil des if-Strings hinzufügen. Weiß jemand, warum es als [[ "$@" -eq ^[0-9] ]] geschrieben werden muss, während der erste Teil mit nur einem [] geschrieben werden kann?

Antwort1

Zuerst [ "$#" -eq ^[0-9] ]oder [ "$@" -eq ^[0-9] ](die Frage wurde bearbeitet, während ich meine Antwort verfasste…). Aus dem Kontext schließe ich, dass Sie testen möchten, ob der Parameter eine Zahl ist.

  • $#ist immer eine Zahl, die Anzahl der Positionsparameter in Dezimalzahlen. Sie möchten zwei Positionsparameter testen: $1und $2.
  • [ist ein Befehl ( [ whatever ]ist gleichwertig mit test whatever). Es kann ein integriertes Shell-Element sein, aber syntaktisch ist es wie lsoder man. Nachfolgende Wörter (einschließlich ]) werden von der Shell wie alle Wörter analysiert, die einem Befehl folgen: Die Shell erweitert sie ( $#wird z. B. zu einer Zahl), entfernt Anführungszeichen und wird schließlich zu Parametern von [. Eine dieser Erweiterungen ist die Dateinamenerweiterung, bei der verschiedene Muster mit vorhandenen Dateinamen abgeglichen werden (Dateinamenerweiterung, Globbing). Ohne Anführungszeichen ^[0-9]kann zu erweitert werden ^2 ^5, wenn diese Dateien vorhanden sind. Wenn es keine Übereinstimmung gibt oder Sie die Funktion deaktivieren, ^[0-9]wird [wörtlich zu (als eines seiner Argumente) abgerufen. Es sieht wie ein Muster aus, [unterstützt aber ohnehin keinen Musterabgleich.
  • -eqerfordert, dass das vorherige und das nächste Argument ganze Zahlen sind. $#wird mit Sicherheit zu einer ganzen Zahl erweitert. In diesem Kontext gibt es keine Möglichkeit, ^[0-9]zu einer Zahl zu erweitern.
  • Doppelte Anführungszeichen $@sind etwas Besonderes, sie können trotz Anführungszeichen auf mehrere Wörter erweitert werden („Felder“ im Sinne von POSIX). Normalerweise setzen wir Anführungszeichen, um eine Worttrennung zu verhindern: "$foo"wird auf ein Wort erweitert, auch wenn der Wert Leerzeichen usw. enthält. "$@"wird wie erweitert "$1" "$2" …, seine doppelten Anführungszeichen verhindern $1die Erweiterung auf mehrere Wörter (und separat $2usw.). In diesem Fragment:

    [ "$#" -eq 2 ] && [ "$@" -eq ^[0-9] ]
    

    der zweite Test [wird ausgeführt, wenn der erste erfolgreich ist (so &&funktioniert es), d. h. wenn genau zwei Positionsparameter vorhanden sind. In diesem Fall "$@"ist äquivalent zu "$1" "$2"und der zweite Test wird

    [ "$1" "$2" -eq ^[0-9] ]
    

    Das ergibt keinen Sinn. [Je nach den tatsächlichen Parameterwerten und der Erweiterung kann es zu unterschiedlichen Beschwerden kommen ^[0-9], aber es ist trotzdem Müll.


Nun zu [[ "$#" = [0-9] ]].

Hinweis: Dieser Ausschnitt war in der ersten Überarbeitung der Frage enthalten. Die Frage wurde bearbeitet, während ich meine Antwort verfasste. Ich habe mich entschieden, diesen Ausschnitt beizubehalten, da er [[irgendwann trotzdem nützlich sein könnte.

  • Nochmals: $#ist immer eine Zahl.
  • [[ist nicht gleichbedeutend mit [. Jeder sinnvolle Test, den Sie mit durchführen können, [kann mit neu geschrieben werden [[(obwohl Sie dies nicht gedankenlos tun sollten, indem Sie einfach [durch ersetzen [[). Es gibt zusätzliche Tests [[, die Sie durchführen können, es ist leistungsfähiger. Während [(und test) von POSIX gefordert wird, [[ist dies nicht der Fall.

    Wichtig: [[ist kein Befehl wie lsoder [. Es ist ein Schlüsselwort, das die Art und Weise ändert, wie die Shell die folgenden Wörter analysiert. Alles zwischen [[und ]]wird anders analysiert.

    Wenn der Operator ist =, ist das nächste Argument ein Muster. Einige Zeichen/Syntaxen (z. B. *oder [0-9]) werden, wenn sie nicht in Anführungszeichen stehen, mit dem Argument vor verglichen =. Es ist wie die Dateinamenerweiterung, aber nicht mit vorhandenen Dateinamen. Ihr Code hat getestet, ob (erweitert) $#genau eine Ziffer ist. Der gesamte Test war

    [[ "$#" -eq 2 ]] && [[ "$#" = [0-9] ]]
    

    foo && barläuft bar, wenn es fooerfolgreich ist. Der zweite [[Test wurde nur durchgeführt, wenn $#er auf erweitert wurde 2, also musste er erfolgreich sein, da 2es genau eine Ziffer ist. In diesem Fall [[ändert der zweite Test nichts.


Nehmen wir an, Sie haben Ihren Fehler erkannt und getestet, ob es $1sich um eine Zahl handelt.

  • Wie oben [[ "$1" = [0-9] ]]wird Erfolg zurückgegeben, wenn $1die Erweiterung auf genau eine Ziffer erfolgt.
  • [[ "$1" = [0-9][0-9] ]]– genau zwei Ziffern.
  • [[ "$1" = [0-9]* ]]– etwas, das mit einer Ziffer beginnt.

Um eine beliebige Anzahl von Ziffern zu erkennen, benötigen Sie =~. Dieser Operator im Inneren [[ ]]ist etwas ähnlich zu =, aber jetzt ist das Muster ein erweiterter regulärer Ausdruck. Es muss jedoch nicht mit der gesamten Zeichenfolge links vom Operator übereinstimmen, eine Teilzeichenfolge reicht aus (deshalb werden in Ihrem Fall ^und Anker benötigt):$

  • [[ "$1" =~ [0-9] ]]Gibt Erfolg zurück, wenn $1es zu einer Zeichenfolge erweitert wird, die eine Ziffer enthält.
  • [[ "$1" =~ [0-9][0-9] ]]– … die aus zwei nebeneinander liegenden Ziffern bestehen.
  • [[ "$1" =~ ^[0-9]$ ]]– eine Zeichenfolge, die genau aus einer Ziffer besteht.
  • [[ "$1" =~ ^[0-9]*$ ]]– null oder mehr Ziffern.
  • [[ "$1" =~ ^[0-9][0-9]*$ ]]– eine oder mehrere Ziffern.
  • [[ "$1" =~ ^[0-9]+$ ]]– eine oder mehrere Ziffern.

[[ "$1" =~ ^[0-9]+$ ]]und [[ "$2" =~ ^[0-9]+$ ]]sind die Tests, die Sie möglicherweise möchten.Um führende zu ermöglichen, -ändern Sie sie wie folgt: [[ "$1" =~ ^-?[0-9]+$ ]]. Das gesamte Testfragment kann

[ "$#" -eq 2 ] && `[[ "$1" =~ ^-?[0-9]+$ ]]` && `[[ "$2" =~ ^-?[0-9]+$ ]]`

Innerhalb [[ ]]von Variablen/Parametern dürfen keine doppelten Anführungszeichen verwendet werden und ihre erweiterten Werte werden nicht in Wörter aufgeteilt und geglobbing. Sie können also [[ $1 =~ ^-?[0-9]+$ ]]sicher schreiben. Dies ist eine Ausnahme; im Allgemeinen sollten Sie Variablen in doppelte Anführungszeichen setzen. Hier stören doppelte Anführungszeichen nicht, daher empfehle ich, sie trotzdem zu verwenden, um sich an diese gute allgemeine Vorgehensweise zu gewöhnen.


Sie behaupten, Sie hätten das Problem mit gelöst [[ "$@" -eq ^[0-9] ]]. Tut mir leid, das glaube ich Ihnen nicht. Wenn es genau zwei Positionsparameter gibt und diese Zahlen sind, sehe ich keine Möglichkeit, dies syntaktisch korrekt zu machen, geschweige denn zu testen, was Sie wollen. In anderen Fällen … sehe ich immer noch keine Möglichkeit.

… using [[ "$@" =~ ^[0-9] ]](aus Ihrem Kommentar, der nicht mit der Frage zusammenhängt). Dies ist keine richtige Lösung. Meine Tests zeigen, dass sich "$@"inside [[ ]]wie unquoted verhält $@, das als ein Wort behandelt wird, ohne Worttrennung und Globbing. Dies ist sehr ähnlich dem Verhalten regulärer Variablen inside [[ ]], obwohl es mir im Voraus nicht klar war, weil ich weiß, "$@"dass es nicht wie reguläre Variablen ist.

Randbemerkung: Aus diesem Grund wollte ich nie "$@"inside verwenden [[ ]]. Ich kann mir kein Szenario vorstellen, in dem es das Richtige wäre.

Wenn genau zwei Positionsparameter vorhanden sind, lautet Ihre „Lösung“ [[ "$1 $2" =~ ^[0-9] ]]. Sie ist syntaktisch gültig und prüft, ob das allererste Zeichen des ersten Arguments Ihres Skripts eine Ziffer ist. Nur dies. Führen Sie aus ./scriptgoeshere.sh 0h well, die Argumente bestehen den Test.


Anmerkungen:

  • [0-9](als Globbing-Muster oder regulärer Ausdruck) hängt von Ihrem aktuellen Gebietsschema ab. Höchstwahrscheinlich wird es wie erwartet funktionieren, aber technisch gesehen ist es möglich, ein Gebietsschema zu haben, bei dem 9vor 0oder 2nach steht 0-9. Die allgemeinste Möglichkeit, eine Ziffer zu vergleichen, ist [[:digit:]]. SieheDas.

    Andererseits möchten Sie vielleicht $1und/oder $2mit Shell-Arithmetik ( $((…))) [ "$1" -ge 50 ]oder Ähnlichem verwenden. Dann benötigen Sie nur arabische Ziffern. Selbst wenn [[:digit:]]diese in Ihrem Gebietsschema enthalten sind (wahrscheinlich ist das der Fall), können auch andere Ziffern enthalten sein. Bedenken Sie [0123456789]; es ist explizit und nicht gebietsschemaabhängig.

  • Nicht jede übereinstimmende Zeichenfolge ^-?[0-9]+$wird aus der Sicht von [und/oder als Ganzzahl betrachtet [[(z. B. wenn Sie dies tun [ "$1" -eq 0 ]). Jedes Tool kann einen Bereich von Ganzzahlen verarbeiten. Ebenso kann die Shell-Arithmetik mathematisch falsche Ergebnisse liefern, wenn Sie ihr eine Ziffernfolge zuführen, die eine Ganzzahl außerhalb des Bereichs beschreibt.

  • Mathematisch gesehen 1.00ist es eine Ganzzahl. Manche Tools akzeptieren es als Ganzzahl, andere nicht. Was ist mit 1e100?

verwandte Informationen