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

112voto

Matt J Punkte 41229

getopt() / getopts() ist eine gute Option. Kopiert von aquí :

Die einfache Verwendung von "getopt" wird in diesem Mini-Skript gezeigt:

#!/bin/bash
echo "Before getopt"
for i
do
  echo $i
done
args=`getopt abc:d $*`
set -- $args
echo "After getopt"
for i
do
  echo "-->$i"
done

Wir haben gesagt, dass jede der Optionen -a, -b, -c oder -d erlaubt ist, aber dass -c von einem Argument gefolgt wird (das "c:" sagt das).

Nennen wir dies "g" und probieren es aus:

bash-2.05a$ ./g -abc foo
Before getopt
-abc
foo
After getopt
-->-a
-->-b
-->-c
-->foo
-->--

Wir beginnen mit zwei Argumenten, und "getopt" trennt die Optionen auf und setzt jede in ihr eigenes Argument. Es hat auch "--" hinzugefügt.

5 Stimmen

Verwendung von $* ist die gebrochene Verwendung von getopt . (Es schließt Argumente mit Leerzeichen ein.) Siehe meine Antwort für die ordnungsgemäße Verwendung.

0 Stimmen

Warum sollten Sie es noch komplizierter machen wollen?

0 Stimmen

@Matt J, der erste Teil des Skripts (für i) könnte Argumente mit Leerzeichen verarbeiten, wenn Sie "$i" anstelle von $i verwenden. Getopts scheint nicht in der Lage zu sein, Argumente mit Leerzeichen zu verarbeiten. Was wäre der Vorteil von getopt gegenüber der for i-Schleife?

54voto

bubla Punkte 893

Ich habe das Problem, portables Parsing in Skripte zu schreiben, als so frustrierend empfunden, dass ich geschrieben habe Argbash - ein FOSS-Code-Generator, der den Code zum Parsen von Argumenten für Ihr Skript generieren kann und einige nette Funktionen hat:

https://argbash.io

0 Stimmen

Danke, dass Sie argbash geschrieben haben, ich habe es gerade benutzt und finde, dass es gut funktioniert. Ich habe mich für argbash entschieden, weil es ein Codegenerator ist, der die ältere bash 3.x unterstützt, die unter OS X 10.11 El Capitan zu finden ist. Der einzige Nachteil ist, dass die Code-Generator-Ansatz bedeutet eine Menge Code in Ihrem Haupt-Skript, im Vergleich zum Aufruf eines Moduls.

2 Stimmen

Sie können Argbash so verwenden, dass es eine maßgeschneiderte Parsing-Bibliothek für Sie erstellt, die Sie in Ihr Skript einbinden können, oder Sie können sie in einer separaten Datei speichern und als Quelle verwenden. Ich habe eine Beispiel um das zu demonstrieren, und ich habe es auch in der Dokumentation deutlicher gemacht.

1 Stimmen

Gut zu wissen. Das Beispiel ist interessant, aber immer noch nicht wirklich klar - vielleicht können Sie den Namen des generierten Skripts in "parse_lib.sh" oder ähnlich ändern und zeigen, wo das Hauptskript es aufruft (wie im Abschnitt über das Wrapping-Skript, das einen komplexeren Anwendungsfall darstellt).

44voto

Shane Day Punkte 465

Ich habe die früheren Antworten als Ausgangspunkt verwendet, um mein altes Adhoc-Param-Parsing aufzuräumen. Dann habe ich den folgenden Vorlagencode umstrukturiert. Er verarbeitet sowohl lange als auch kurze Parameter mit = oder durch Leerzeichen getrennten Argumenten sowie mehrere kurze Parameter, die in Gruppen zusammengefasst sind. Schließlich fügt er alle Nicht-Param-Argumente wieder in die Variablen $1,$2 ein.

#!/usr/bin/env bash

# NOTICE: Uncomment if your script depends on bashisms.
#if [ -z "$BASH_VERSION" ]; then bash $0 $@ ; exit $? ; fi

echo "Before"
for i ; do echo - $i ; done

# Code template for parsing command line parameters using only portable shell
# code, while handling both long and short params, handling '-f file' and
# '-f=file' style param data and also capturing non-parameters to be inserted
# back into the shell positional parameters.

while [ -n "$1" ]; do
        # Copy so we can modify it (can't modify $1)
        OPT="$1"
        # Detect argument termination
        if [ x"$OPT" = x"--" ]; then
                shift
                for OPT ; do
                        REMAINS="$REMAINS \"$OPT\""
                done
                break
        fi
        # Parse current opt
        while [ x"$OPT" != x"-" ] ; do
                case "$OPT" in
                        # Handle --flag=value opts like this
                        -c=* | --config=* )
                                CONFIGFILE="${OPT#*=}"
                                shift
                                ;;
                        # and --flag value opts like this
                        -c* | --config )
                                CONFIGFILE="$2"
                                shift
                                ;;
                        -f* | --force )
                                FORCE=true
                                ;;
                        -r* | --retry )
                                RETRY=true
                                ;;
                        # Anything unknown is recorded for later
                        * )
                                REMAINS="$REMAINS \"$OPT\""
                                break
                                ;;
                esac
                # Check for multiple short options
                # NOTICE: be sure to update this pattern to match valid options
                NEXTOPT="${OPT#-[cfr]}" # try removing single short opt
                if [ x"$OPT" != x"$NEXTOPT" ] ; then
                        OPT="-$NEXTOPT"  # multiple short opts, keep going
                else
                        break  # long form, exit inner loop
                fi
        done
        # Done with that param. move to next
        shift
done
# Set the non-parameters back into the positional parameters ($1 $2 ..)
eval set -- $REMAINS

echo -e "After: \n configfile='$CONFIGFILE' \n force='$FORCE' \n retry='$RETRY' \n remains='$REMAINS'"
for i ; do echo - $i ; done

0 Stimmen

Dieser Code kann nicht mit Optionen mit solchen Argumenten umgehen: -c1 . Und die Verwendung von = kurze Optionen von ihren Argumenten zu trennen, ist ungewöhnlich...

2 Stimmen

Bei diesem nützlichen Stück Code bin ich auf zwei Probleme gestoßen: 1) das "shift" im Fall von "-c=foo" frisst den nächsten Parameter; und 2) 'c' sollte nicht im "[cfr]"-Muster für kombinierbare Kurzoptionen enthalten sein.

34voto

Ponyboy47 Punkte 894
# As long as there is at least one more argument, keep looping
while [[ $# -gt 0 ]]; do
    key="$1"
    case "$key" in
        # This is a flag type option. Will catch either -f or --foo
        -f|--foo)
        FOO=1
        ;;
        # Also a flag type option. Will catch either -b or --bar
        -b|--bar)
        BAR=1
        ;;
        # This is an arg value type option. Will catch -o value or --output-file value
        -o|--output-file)
        shift # past the key and to the value
        OUTPUTFILE="$1"
        ;;
        # This is an arg=value type option. Will catch -o=value or --output-file=value
        -o=*|--output-file=*)
        # No need to shift here since the value is part of the same string
        OUTPUTFILE="${key#*=}"
        ;;
        *)
        # Do whatever you want with extra options
        echo "Unknown option '$key'"
        ;;
    esac
    # Shift after checking all the cases to get the next option
    shift
done

So können Sie sowohl durch Leerzeichen getrennte Optionen/Werte als auch gleich definierte Werte haben.

Sie könnten also Ihr Skript mit ausführen:

./myscript --foo -b -o /fizz/file.txt

wie auch:

./myscript -f --bar -o=/fizz/file.txt

und beide sollten das gleiche Endergebnis haben.

PROS:

  • Erlaubt sowohl -arg=Wert als auch -arg-Wert

  • Funktioniert mit jedem Arg-Namen, den man in der Bash verwenden kann

    • Bedeutet -a oder -arg oder --arg oder -a-r-g oder was auch immer
  • Reiner Bash. Keine Notwendigkeit, getopt oder getopts zu lernen/verwenden

CONS:

  • Args können nicht kombiniert werden

    • Das bedeutet kein -abc. Sie müssen -a -b -c machen

0 Stimmen

Ich habe da eine Frage. Warum haben Sie shift; OUTPUTFILE="$1" anstelle von OUTPUTFILE="$2" ? Vielleicht hat es eine einfache Antwort, aber ich bin ein Neuling in bash

1 Stimmen

Ich glaube, man kann beides machen, und es kommt wirklich nur auf die persönlichen Vorlieben an. In diesem Fall wollte ich einfach die $1 als das "aktive" Argument überall

33voto

phyatt Punkte 17614

Dieses Beispiel zeigt, wie man getopt y eval y HEREDOC y shift um kurze und lange Parameter mit und ohne einen nachfolgenden erforderlichen Wert zu behandeln. Auch die switch/case-Anweisung ist prägnant und leicht zu verstehen.

#!/usr/bin/env bash

# usage function
function usage()
{
   cat << HEREDOC

   Usage: $progname [--num NUM] [--time TIME_STR] [--verbose] [--dry-run]

   optional arguments:
     -h, --help           show this help message and exit
     -n, --num NUM        pass in a number
     -t, --time TIME_STR  pass in a time string
     -v, --verbose        increase the verbosity of the bash script
     --dry-run            do a dry run, dont change any files

HEREDOC
}  

# initialize variables
progname=$(basename $0)
verbose=0
dryrun=0
num_str=
time_str=

# use getopt and store the output into $OPTS
# note the use of -o for the short options, --long for the long name options
# and a : for any option that takes a parameter
OPTS=$(getopt -o "hn:t:v" --long "help,num:,time:,verbose,dry-run" -n "$progname" -- "$@")
if [ $? != 0 ] ; then echo "Error in command line arguments." >&2 ; usage; exit 1 ; fi
eval set -- "$OPTS"

while true; do
  # uncomment the next line to see how shift is working
  # echo "\$1:\"$1\" \$2:\"$2\""
  case "$1" in
    -h | --help ) usage; exit; ;;
    -n | --num ) num_str="$2"; shift 2 ;;
    -t | --time ) time_str="$2"; shift 2 ;;
    --dry-run ) dryrun=1; shift ;;
    -v | --verbose ) verbose=$((verbose + 1)); shift ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

if (( $verbose > 0 )); then

   # print out all the parameters we read in
   cat <<EOM
   num=$num_str
   time=$time_str
   verbose=$verbose
   dryrun=$dryrun
EOM
fi

# The rest of your script below

Die wichtigsten Zeilen des obigen Skripts sind diese:

OPTS=$(getopt -o "hn:t:v" --long "help,num:,time:,verbose,dry-run" -n "$progname" -- "$@")
if [ $? != 0 ] ; then echo "Error in command line arguments." >&2 ; exit 1 ; fi
eval set -- "$OPTS"

while true; do
  case "$1" in
    -h | --help ) usage; exit; ;;
    -n | --num ) num_str="$2"; shift 2 ;;
    -t | --time ) time_str="$2"; shift 2 ;;
    --dry-run ) dryrun=1; shift ;;
    -v | --verbose ) verbose=$((verbose + 1)); shift ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

Kurz, prägnant, gut lesbar und behandelt so ziemlich alles (IMHO).

Ich hoffe, das hilft jemandem.

4 Stimmen

Dies ist eine der besten Antworten.

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