Wie schreibe ich ein Skript, das mit oder ohne Argumente arbeitet?

13

Ich habe ein Bash-Skript, das ungefähr so ​​aussieht:

#!/bin/bash

if [ $1 = "--test" ] || [ $1 = "-t" ]; then
    echo "Testing..."
    testing="y"

else
    testing="n"
    echo "Not testing."

fi

Ich möchte es also nicht nur mit ./script --testoder ausführen können ./script -t, sondern auch ohne Argumente (nur ./script). Wenn ich das mit dem aktuellen Code mache, ist die Ausgabe anscheinend einfach:

./script: line 3: [: =: unary operator expected
./script: line 3: [: =: unary operator expected
Not testing.

Wie programmiere ich es also so, dass es ohne Argumente funktioniert, elseohne den Fehler auszulösen? Was mache ich falsch?

Peter Cordes
quelle
1
Sie benötigen doppelte Klammern um Ihre Schecks. [[]]
Terrance
3
Siehe Bash Fallstricke # 4
Steeldriver
@Terrance du brauchst sie nicht, obwohl du sie verwenden solltest, wenn du auf bash zielst .
Ruslan

Antworten:

1

Die "richtige Art", lange Optionen in Shell- Skripten zu verwenden, ist das GNU-Dienstprogramm getopt . Es gibt auch getopts, die eine eingebaute Bash-Funktion sind , die jedoch nur kurze Optionen wie zulässt -t. Einige getoptAnwendungsbeispiele finden Sie hier .

Hier ist ein Skript, das zeigt, wie ich mit Ihrer Frage umgehen würde. Erläuterungen zu den meisten Schritten werden als Kommentare im Skript selbst hinzugefügt.

#!/bin/bash

# GNU getopt allows long options. Letters in -o correspond to
# comma-separated list given in --long.

opts=$(getopt -o t --long test -- "$*")
test $? -ne 0 && exit 2 # error happened

set -- $opts # some would prefer using eval set -- "$opts"
# if theres -- as first argument, the script is called without
# option flags
if [ "$1" = "--"  ]; then
    echo "Not testing"
    testing="n"
    # Here we exit, and avoid ever getting to argument parsing loop
    # A more practical case would be to call a function here
    # that performs for no options case
    exit 1
fi

# Although this question asks only for one 
# option flag, the proper use of getopt is with while loop
# That's why it's done so - to show proper form.
while true; do
    case "$1" in
        # spaces are important in the case definition
        -t | --test ) testing="y"; echo "Testing" ;;
    esac
    # Loop will terminate if there's no more  
    # positional parameters to shift
    shift  || break
done

echo "Out of loop"

Mit ein paar Vereinfachungen und entfernten Kommentaren kann dies zu Folgendem zusammengefasst werden:

#!/bin/bash
opts=$(getopt -o t --long test -- "$*")
test $? -ne 0 && exit 2 # error happened
set -- $opts    
case "$1" in
    -t | --test ) testing="y"; echo "Testing";;
    --) testing="n"; echo "Not testing";;
esac
Sergiy Kolodyazhnyy
quelle
16

Verschiedene Wege; Die zwei offensichtlichsten sind:

  • Setzen Sie $1doppelte Anführungszeichen:if [ "$1" = "--test" ]
  • Überprüfen Sie die Anzahl der Argumente mit $ #

Besser noch, benutze getopts.

JayEye
quelle
2
+1 für die Verwendung von getopts. Das ist der beste Weg.
Seth
3
Eine andere gebräuchliche Redewendung besteht [ "x$1" = "x--test" ]darin, Befehlszeilenargumente, die gültige Operatoren für den [Befehl sind, erneut zu verteidigen . (Dies ist ein weiterer Grund, warum [[ ]]es bevorzugt wird: Es ist Teil der Shell-Syntax, nicht nur ein eingebauter Befehl.)
Peter Cordes
7

Sie müssen Ihre Variablen in der if-Bedingung angeben. Ersetzen:

if [ $1 = "--test" ] || [ $1 = "-t" ]; then

mit:

if [ "$1" = '--test' ] || [ "$1" = '-t' ]; then  

Dann klappt es:

➜ ~ ./test.sh Testet
nicht.

Setzen Sie Ihre Variablen immer in doppelte Anführungszeichen!

Seth
quelle
Sind einfache Anführungszeichen wichtig oder kann man stattdessen doppelte Anführungszeichen verwenden?
Die genaue Antwort hängt davon ab, welche Shell Sie verwenden. Aufgrund Ihrer Frage gehe ich jedoch davon aus, dass Sie einen Bourne-Shell-Nachkommen verwenden. Der Hauptunterschied zwischen einfachen und doppelten Anführungszeichen besteht darin, dass die variable Erweiterung in doppelten Anführungszeichen erfolgt, nicht jedoch in einfachen Anführungszeichen: $ FOO = bar $ echo "$ FOO" bar $ echo '$ FOO' $ FOO (hmm .... can ' t
Formatcode
@ParanoidPanda Sie müssen keine einfachen Anführungszeichen verwenden, nein, es empfiehlt sich jedoch, sie für Zeichenfolgenliterale zu verwenden. Auf diese Weise werden Sie später nicht überrascht, wenn Ihre String-Daten einen Symbol-Bash enthalten, der erweitert werden soll.
Seth
@ParanoidPanda Was @JayEye sagte: Mit einfachen Anführungszeichen gibt es keine variable Erweiterung foo=bar echo '$foo'druckt $fooan den Ausgang, sondern foo=bar echo "$foo"druckt barstatt.
EKons
4

Sie verwenden Bash. Bash hat eine gute Alternative zu [: [[. Mit [[müssen Sie sich keine Gedanken über das Zitieren machen, und Sie erhalten viel mehr Operatoren als [, einschließlich der richtigen Unterstützung für ||:

if [[ $1 = "--test" || $1 = "-t" ]]
then
    echo "Testing..."
    testing="y"
else
    testing="n"
    echo "Not testing."
fi

Oder Sie können reguläre Ausdrücke verwenden:

if [[ $1 =~ ^(--test|-t) ]]
then
    echo "Testing..."
    testing="y"
else
    testing="n"
    echo "Not testing."
fi

Natürlich sollte immer korrekt zitiert werden, aber es gibt keinen Grund, [bei der Verwendung von Bash zu bleiben .

muru
quelle
3

So testen Sie, ob Argumente vorhanden sind (im Allgemeinen und führen Sie die entsprechenden Aktionen aus):

#!/bin/bash

check=$1

if [ -z "$check" ]; then
  echo "no arguments"
else echo "there was an argument"
fi
Jacob Vlijm
quelle
Warum nicht [ -z "$1" ]?
EKons
@ ΈρικΚωνσταντόπουλος In diesem Fall, normalerweise jedoch in Skripten, nenne ich die Variable ein einziges Mal, verweise darauf in den Prozeduren, was normalerweise mehr als einmal ist. Beschlossen, das Protokoll in der Probe zu behalten.
Jacob Vlijm
In diesem Beispiel scheinen Sie $checkvor allen anderen shifts nur einmal zu verwenden , daher ist es besser, $1stattdessen zu verwenden .
EKons
@ ΈρικΚωνσταντόπουλος Natürlich nicht, wie erwähnt, es ist ein Beispiel , um in Skripten verwendet zu werden. In Skripten ist es üblich, dasselbe immer und immer wieder zu tun.
Jacob Vlijm
Ich verstehe, was Sie meinen, aber es ist besser, #!bei der Bereitstellung von Beispielen keine Shebangs zu verwenden (Sie sollten die Shell außerhalb des Codes beschreiben).
EKons