2542 Stimmen

Wie analysiere ich Kommandozeilenargumente in der Bash?

Angenommen, ich habe ein Skript, das mit dieser Zeile aufgerufen wird:

./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile

oder dieses:

./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile 

Wie kann man dies so interpretieren, dass in jedem Fall (oder einer Kombination von beiden) $v , $f y $d werden alle auf true y $outFile wird gleich sein mit /fizz/someOtherFile ?

1 Stimmen

Für zsh-Benutzer gibt es ein großartiges builtin namens zparseopts, das das kann: zparseopts -D -E -M -- d=debug -debug=d Und haben beide -d y --debug im $debug Array echo $+debug[1] gibt 0 oder 1 zurück, wenn einer dieser Werte verwendet wird. Ref: zsh.org/mla/users/2011/msg00350.html

2 Stimmen

Ein wirklich gutes Tutorial: linuxcommand.org/lc3_wss0120.php . Besonders gut gefällt mir das Beispiel "Befehlszeilenoptionen".

0 Stimmen

Ich habe ein Skript erstellt, das dies für Sie erledigt, es heißt - github.com/unfor19/bargs

1voto

Mike Q Punkte 5731

Auch das könnte nützlich sein: Sie können einen Wert festlegen und, wenn jemand eine Eingabe macht, den Standardwert mit diesem Wert überschreiben.

myscript.sh -f ./serverlist.txt oder einfach ./myscript.sh (und sie nimmt Standardwerte an)

    #!/bin/bash
    # --- set the value, if there is inputs, override the defaults.

    HOME_FOLDER="${HOME}/owned_id_checker"
    SERVER_FILE_LIST="${HOME_FOLDER}/server_list.txt"

    while [[ $# > 1 ]]
    do
    key="$1"
    shift

    case $key in
        -i|--inputlist)
        SERVER_FILE_LIST="$1"
        shift
        ;;
    esac
    done

    echo "SERVER LIST   = ${SERVER_FILE_LIST}"

1voto

Daniel Bigham Punkte 177

Die oberste Antwort auf diese Frage schien ein wenig fehlerhaft zu sein, als ich sie ausprobierte - hier ist meine Lösung, die sich als robuster erwiesen hat:

boolean_arg=""
arg_with_value=""

while [[ $# -gt 0 ]]
do
key="$1"
case $key in
    -b|--boolean-arg)
    boolean_arg=true
    shift
    ;;
    -a|--arg-with-value)
    arg_with_value="$2"
    shift
    shift
    ;;
    -*)
    echo "Unknown option: $1"
    exit 1
    ;;
    *)
    arg_num=$(( $arg_num + 1 ))
    case $arg_num in
        1)
        first_normal_arg="$1"
        shift
        ;;
        2)
        second_normal_arg="$1"
        shift
        ;;
        *)
        bad_args=TRUE
    esac
    ;;
esac
done

# Handy to have this here when adding arguments to
# see if they're working. Just edit the '0' to be '1'.
if [[ 0 == 1 ]]; then
    echo "first_normal_arg: $first_normal_arg"
    echo "second_normal_arg: $second_normal_arg"
    echo "boolean_arg: $boolean_arg"
    echo "arg_with_value: $arg_with_value"
    exit 0
fi

if [[ $bad_args == TRUE || $arg_num < 2 ]]; then
    echo "Usage: $(basename "$0") <first-normal-arg> <second-normal-arg> [--boolean-arg] [--arg-with-value VALUE]"
    exit 1
fi

1voto

Ich verwende es zur Iteration über Schlüssel => Wert vom Ende her. Ein erstes optionales Argument wird nach der Schleife abgefangen.

Die Verwendung ist ./script.sh fakultativ-erste-arg -Schlüssel Wert -Schlüssel2 Wert2

#!/bin/sh

a=$(($#-1))
b=$(($#))
while [ $a -gt 0 ]; do
    eval 'key="$'$a'"; value="$'$b'"'
    echo "$key => $value"
    b=$(($b-2))
    a=$(($a-2))
done
unset a b key value

[ $(($#%2)) -ne 0 ] && echo "first_arg = $1"

Natürlich kann man es mit ein paar Änderungen von links nach rechts machen.

Dieser Codeschnipsel zeigt die Schlüssel => Wert Paare und das erste Argument, falls es existiert.

#!/bin/sh

a=$((1+$#%2))
b=$((1+$a))

[ $(($#%2)) -ne 0 ] && echo "first_arg = $1"

while [ $a -lt $# ]; do
    eval 'key="$'$a'"; value="$'$b'"'
    echo "$key => $value"
    b=$(($b+2))
    a=$(($a+2))
done

unset a b key value

Getestet mit 100.000 Argumenten, schnell.

Sie können auch iterieren Schlüssel => Wert y erstes optionales Argument von links nach rechts ohne eval :

#!/bin/sh

a=$(($#%2))
b=0

[ $a -eq 1 ] && echo "first_arg = $1"

for value; do
    if [ $b -gt $a -a $(($b%2)) -ne $a ]; then
        echo "$key => $value"
    fi
    key="$value"
    b=$((1+$b))
done

unset a b key value

1voto

phk Punkte 1913

Eine andere Lösung ohne getopt[s], POSIX, alter Unix-Stil

Ähnlich wie die von Bruno Bronosky gepostete Lösung dies hier ist eine ohne die Verwendung von getopt(s) .

Das Hauptunterscheidungsmerkmal meiner Lösung ist, dass sie es erlaubt, Optionen miteinander zu verketten, genau wie tar -xzf foo.tar.gz ist gleich tar -x -z -f foo.tar.gz . Und genau wie in tar , ps usw. ist der führende Bindestrich für einen Block kurzer Optionen optional (kann aber leicht geändert werden). Lange Optionen werden ebenfalls unterstützt (wenn ein Block mit einer Option beginnt, sind jedoch zwei führende Bindestriche erforderlich).

Code mit Beispieloptionen

#!/bin/sh

echo
echo "POSIX-compliant getopt(s)-free old-style-supporting option parser from phk@[se.unix]"
echo

print_usage() {
  echo "Usage:

  $0 {a|b|c} [ARG...]

Options:

  --aaa-0-args
  -a
    Option without arguments.

  --bbb-1-args ARG
  -b ARG
    Option with one argument.

  --ccc-2-args ARG1 ARG2
  -c ARG1 ARG2
    Option with two arguments.

" >&2
}

if [ $# -le 0 ]; then
  print_usage
  exit 1
fi

opt=
while :; do

  if [ $# -le 0 ]; then

    # no parameters remaining -> end option parsing
    break

  elif [ ! "$opt" ]; then

    # we are at the beginning of a fresh block
    # remove optional leading hyphen and strip trailing whitespaces
    opt=$(echo "$1" | sed 's/^-\?\([a-zA-Z0-9\?-]*\)/\1/')

  fi

  # get the first character -> check whether long option
  first_chr=$(echo "$opt" | awk '{print substr($1, 1, 1)}')
  [ "$first_chr" = - ] && long_option=T || long_option=F

  # note to write the options here with a leading hyphen less
  # also do not forget to end short options with a star
  case $opt in

    -)

      # end of options
      shift
      break
      ;;

    a*|-aaa-0-args)

      echo "Option AAA activated!"
      ;;

    b*|-bbb-1-args)

      if [ "$2" ]; then
        echo "Option BBB with argument '$2' activated!"
        shift
      else
        echo "BBB parameters incomplete!" >&2
        print_usage
        exit 1
      fi
      ;;

    c*|-ccc-2-args)

      if [ "$2" ] && [ "$3" ]; then
        echo "Option CCC with arguments '$2' and '$3' activated!"
        shift 2
      else
        echo "CCC parameters incomplete!" >&2
        print_usage
        exit 1
      fi
      ;;

    h*|\?*|-help)

      print_usage
      exit 0
      ;;

    *)

      if [ "$long_option" = T ]; then
        opt=$(echo "$opt" | awk '{print substr($1, 2)}')
      else
        opt=$first_chr
      fi
      printf 'Error: Unknown option: "%s"\n' "$opt" >&2
      print_usage
      exit 1
      ;;

  esac

  if [ "$long_option" = T ]; then

    # if we had a long option then we are going to get a new block next
    shift
    opt=

  else

    # if we had a short option then just move to the next character
    opt=$(echo "$opt" | awk '{print substr($1, 2)}')

    # if block is now empty then shift to the next one
    [ "$opt" ] || shift

  fi

done

echo "Doing something..."

exit 0

Beispiele für die Verwendung finden Sie weiter unten.

Position der Optionen mit Argumenten

Dort müssen die Optionen mit Argumenten nicht die letzten sein (nur lange Optionen müssen es sein). Während also z.B. in tar (zumindest in einigen Implementierungen) die f muss an letzter Stelle stehen, da der Dateiname folgt ( tar xzf bar.tar.gz funktioniert aber tar xfz bar.tar.gz nicht), ist dies hier nicht der Fall (siehe die späteren Beispiele).

Mehrere Optionen mit Argumenten

Als weiterer Bonus werden die Optionsparameter in der Reihenfolge der Optionen von den Parametern mit den erforderlichen Optionen verbraucht. Schauen Sie sich einfach die Ausgabe meines Skripts mit der Befehlszeile an abc X Y Z (oder -abc X Y Z ):

Option AAA activated!
Option BBB with argument 'X' activated!
Option CCC with arguments 'Y' and 'Z' activated!

Auch lange Optionen werden verkettet

Sie können auch lange Optionen in Optionsblöcken verwenden, sofern sie am Ende des Blocks stehen. Die folgenden Befehlszeilen sind also alle gleichwertig (einschließlich der Reihenfolge, in der die Optionen und ihre Argumente verarbeitet werden):

  • -cba Z Y X
  • cba Z Y X
  • -cb-aaa-0-args Z Y X
  • -c-bbb-1-args Z Y X -a
  • --ccc-2-args Z Y -ba X
  • c Z Y b X a
  • -c Z Y -b X -a
  • --ccc-2-args Z Y --bbb-1-args X --aaa-0-args

All dies führt zu:

Option CCC with arguments 'Z' and 'Y' activated!
Option BBB with argument 'X' activated!
Option AAA activated!
Doing something...

Nicht in dieser Lösung

Optionale Argumente

Optionen mit optionalen Argumenten sollten mit ein wenig Arbeit möglich sein, z.B. indem man nach vorne schaut, ob es einen Block ohne Bindestrich gibt; der Benutzer müsste dann einen Bindestrich vor jeden Block setzen, der auf einen Block mit einem Parameter folgt, der einen optionalen Parameter hat. Vielleicht ist dies zu kompliziert, um es dem Benutzer mitzuteilen, so dass es besser ist, in diesem Fall nur einen führenden Bindestrich zu verlangen.

Noch komplizierter wird es bei mehreren möglichen Parametern. Ich würde davon abraten, die Optionen so zu gestalten, dass sie versuchen, intelligent zu sein, indem sie feststellen, ob ein Argument dafür geeignet ist oder nicht (z.B. mit einer Option, die nur eine Zahl als optionales Argument annimmt), da dies in der Zukunft scheitern könnte.

Ich persönlich bevorzuge zusätzliche Optionen anstelle von optionalen Argumenten.

Optionsargumente, die mit einem Gleichheitszeichen eingeleitet werden

Genau wie bei optionalen Argumenten bin ich kein Fan davon (BTW, gibt es einen Thread für die Diskussion der Vor- und Nachteile verschiedener Parameterstile?), aber wenn Sie das wollen, könnten Sie es wahrscheinlich selbst implementieren, so wie es bei http://mywiki.wooledge.org/BashFAQ/035#Manual_loop mit einer --long-with-arg=?* case-Anweisung und dann das Entfernen des Gleichheitszeichens (dies ist übrigens die Seite, die sagt, dass die Verkettung von Parametern mit einigem Aufwand möglich ist, aber "es als Übung für den Leser belässt", was mich dazu brachte, sie beim Wort zu nehmen, aber ich begann von vorne).

Sonstige Anmerkungen

POSIX-kompatibel, funktioniert sogar auf alten Busybox-Systemen, mit denen ich zu tun hatte (z.B. mit cut , head y getopts fehlt).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X