Bash Shell Script - Suchen Sie nach einem Flag und ermitteln Sie dessen Wert

78

Ich versuche, ein Shell-Skript zu erstellen, das so ausgeführt werden soll:

script.sh -t application

Zunächst möchte ich in meinem Skript überprüfen, ob das Skript mit dem Flag -t ausgeführt wurde. Wenn es zum Beispiel ohne dieses Flag ausgeführt wurde, möchte ich, dass es einen Fehler macht:

script.sh

Zweitens möchte ich unter der Annahme, dass ein -t-Flag vorhanden ist, den Wert abrufen und in einer Variablen speichern, die ich beispielsweise in meinem Skript verwenden kann:

FLAG="application"

Bisher konnte ich nur Fortschritte machen, indem $ @ alle Befehlszeilenargumente erfasst, aber ich weiß nicht, wie dies mit Flags zusammenhängt oder ob dies überhaupt möglich ist.

Jimmy
quelle
1
Mögliches Duplikat von Wie analysiere ich Befehlszeilenargumente in Bash?
ZomoXYZ

Antworten:

124

Sie sollten dieses getopts- Tutorial lesen .

Beispiel mit einem -aSchalter, für den ein Argument erforderlich ist:

#!/bin/bash

while getopts ":a:" opt; do
  case $opt in
    a)
      echo "-a was triggered, Parameter: $OPTARG" >&2
      ;;
    \?)
      echo "Invalid option: -$OPTARG" >&2
      exit 1
      ;;
    :)
      echo "Option -$OPTARG requires an argument." >&2
      exit 1
      ;;
  esac
done

Wie Greybot sagte ( getopt! = getopts):

Der externe Befehl getopt (1) ist niemals sicher zu verwenden, es sei denn, Sie wissen, dass es sich um GNU getopt handelt, Sie rufen ihn GNU-spezifisch auf und stellen sicher, dass sich GETOPT_COMPATIBLE nicht in der Umgebung befindet. Verwenden Sie stattdessen getopts (Shell Builtin) oder durchlaufen Sie einfach die Positionsparameter.

Gilles Quenot
quelle
1
getoptsist ein bashintegriertes getoptProgramm , aber es gibt auch ein externes Programm, das mit jeder POSIX-kompatiblen Shell funktionieren sollte.
Chepner
3
Der externe Befehl getopt (1) ist niemals sicher zu verwenden, es sei denn, Sie wissen, dass es sich um GNU getopt handelt, Sie rufen ihn GNU-spezifisch auf und stellen sicher, dass sich GETOPT_COMPATIBLE nicht in der Umgebung befindet. Verwenden Sie stattdessen getopts (Shell Builtin) oder durchlaufen Sie einfach die Positionsparameter.
Gilles Quenot
Tut mir leid, ich brauche das für mich. Wenn es sich um eine eingebaute Bash handelt, funktioniert sie für Shell in Debian
Jimmy
Das getoptsTutorial ist einfach und bietet hilfreiche Beispiele. Auf jeden Fall lesenswert, wenn Sie auf diesen Beitrag stoßen.
Steven C. Howell
Einverstanden mit früheren Kommentaren: Das getopts-Tutorial ist die Zeit, die Sie mit dem Lesen verbringen, einschließlich der Kommentare, von denen einige erweiterte / alternative Verwendungsmöglichkeiten bieten, wirklich wert.
François POYER
29

Verwenden Sie $#diese Option, um die Anzahl der Argumente zu ermitteln. Wenn sie ungleich 2 ist, werden nicht genügend Argumente bereitgestellt:

if [ $# -ne 2 ]; then
   usage;
fi

Überprüfen Sie als nächstes, ob $1gleich ist -t, andernfalls wurde ein unbekanntes Flag verwendet:

if [ "$1" != "-t" ]; then
  usage;
fi

Schließlich speichern $2in FLAG:

FLAG=$2

Hinweis: usage()Ist eine Funktion, die die Syntax anzeigt. Zum Beispiel:

function usage {
   cat << EOF
Usage: script.sh -t <application>

Performs some activity
EOF
   exit 1
}
Veger
quelle
Das mag funktionieren, aber was passiert, wenn ich mehrere Flags verwenden möchte?
Killjoy
Es wird komplexer und es ist sinnvoller, den Befehl getopts zu verwenden, wie in der Antwort von @GillesQuenot
Veger
11

Hier ist eine verallgemeinerte einfache Befehlsargumentschnittstelle, die Sie oben in alle Ihre Skripte einfügen können.

#!/bin/bash

declare -A flags
declare -A booleans
args=()

while [ "$1" ];
do
    arg=$1
    if [ "${1:0:1}" == "-" ]
    then
      shift
      rev=$(echo "$arg" | rev)
      if [ -z "$1" ] || [ "${1:0:1}" == "-" ] || [ "${rev:0:1}" == ":" ]
      then
        bool=$(echo ${arg:1} | sed s/://g)
        booleans[$bool]=true
        echo \"$bool\" is boolean
      else
        value=$1
        flags[${arg:1}]=$value
        shift
        echo \"$arg\" is flag with value \"$value\"
      fi
    else
      args+=("$arg")
      shift
      echo \"$arg\" is an arg
    fi
done


echo -e "\n"
echo booleans: ${booleans[@]}
echo flags: ${flags[@]}
echo args: ${args[@]}

echo -e "\nBoolean types:\n\tPrecedes Flag(pf): ${booleans[pf]}\n\tFinal Arg(f): ${booleans[f]}\n\tColon Terminated(Ct): ${booleans[Ct]}\n\tNot Mentioned(nm): ${boolean[nm]}"
echo -e "\nFlag: myFlag => ${flags["myFlag"]}"
echo -e "\nArgs: one: ${args[0]}, two: ${args[1]}, three: ${args[2]}"

Durch Ausführen des Befehls:

bashScript.sh firstArg -pf -myFlag "my flag value" secondArg -Ct: thirdArg -f

Die Ausgabe lautet wie folgt:

"firstArg" is an arg
"pf" is boolean
"-myFlag" is flag with value "my flag value"
"secondArg" is an arg
"Ct" is boolean
"thirdArg" is an arg
"f" is boolean


booleans: true true true
flags: my flag value
args: firstArg secondArg thirdArg

Boolean types:
    Precedes Flag(pf): true
    Final Arg(f): true
    Colon Terminated(Ct): true
    Not Mentioned(nm): 

Flag: myFlag => my flag value

Args: one => firstArg, two => secondArg, three => thirdArg

Grundsätzlich sind die Argumente in Flags, Boolesche Werte und generische Argumente unterteilt. Auf diese Weise kann ein Benutzer die Flags und Booleschen Werte an einer beliebigen Stelle platzieren, solange er die generischen Argumente (falls vorhanden) in der angegebenen Reihenfolge aufbewahrt.

Erlauben Sie mir und jetzt Ihnen, sich nie wieder mit dem Parsen von Bash-Argumenten zu befassen!

Sie können eine aktualisierte Skript sehen hier

Dies war im letzten Jahr enorm nützlich. Es kann jetzt den Bereich simulieren, indem den Variablen ein Bereichsparameter vorangestellt wird.

Nennen Sie einfach das Skript wie

replace() (
  source $FUTIL_REL_DIR/commandParser.sh -scope ${FUNCNAME[0]} "$@"
  echo ${replaceFlags[f]}
  echo ${replaceBooleans[b]}
)

Sieht nicht so aus, als hätte ich den Argumentationsbereich implementiert. Ich bin mir nicht sicher, warum ich ihn wohl noch nicht gebraucht habe.

jozsef morrissey
quelle
2
Dies ist eine großartige Antwort: Ich gehe noch einen Schritt weiter und platziere sie in einem Skript, das ich dann als arg_handler in meiner .bashrc-Datei alias. Dann source arg_handler; arg_handler "$@"
füge
Sollte -pf nicht zwei separate Flags sein, da es nicht durch zwei Striche weitergeht? Wie in p und f.
James Trickey
1

Versuchen Sie shFlags - Erweiterte Befehlszeilen-Flag-Bibliothek für Unix-Shell-Skripte.

http://code.google.com/p/shflags/

Es ist sehr gut und sehr flexibel.

FLAGGENTYPEN: Dies ist eine Liste der DEFINE _ *, die Sie ausführen können. Alle Flags haben einen Namen, einen Standardwert, eine Hilfezeichenfolge und einen optionalen Kurznamen (Ein-Buchstaben-Name). Einige Flags haben andere Argumente, die mit dem Flag beschrieben werden.

DEFINE_string: Nimmt jede Eingabe und stellt sie als Zeichenfolge dar.

DEFINE_boolean: Nimmt normalerweise kein Argument an: Sagen Sie --myflag, um FLAGS_myflag auf true zu setzen, oder --nomyflag, um FLAGS_myflag auf false zu setzen. Alternativ können Sie sagen --myflag = true oder --myflag = t oder --myflag = 0 oder --myflag = false oder --myflag = f oder --myflag = 1 Das Übergeben einer Option hat den gleichen Effekt wie das Übergeben von Option einmal.

DEFINE_float: Nimmt eine Eingabe und stellt sie als Gleitkommazahl dar. Da die Shell Floats per se nicht unterstützt, wird die Eingabe lediglich als gültiger Gleitkommawert validiert.

DEFINE_integer: Nimmt eine Eingabe und stellt sie als Ganzzahl dar.

SPEZIELLE FLAGGEN: Es gibt einige Flags, die eine besondere Bedeutung haben: --help (oder -?) Gibt eine Liste aller Flags auf lesbare Weise aus --flagfile = foo liest Flags von foo. (noch nicht implementiert) - Beendet wie in getopt () die Flag-Verarbeitung

BEISPIELVERWENDUNG:

-- begin hello.sh --
 ! /bin/sh
. ./shflags
DEFINE_string name 'world' "somebody's name" n
FLAGS "$@" || exit $?
eval set -- "${FLAGS_ARGV}"
echo "Hello, ${FLAGS_name}."
-- end hello.sh --

$ ./hello.sh -n Kate
Hello, Kate.

Hinweis: Ich habe diesen Text aus der Shflags-Dokumentation übernommen

Anandkumar
quelle