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

12voto

Ich gebe Ihnen The Function parse_params das die Parameter aus der Befehlszeile auswertet.

  1. Es handelt sich um eine reine Bash-Lösung, ohne zusätzliche Hilfsprogramme.
  2. Verschmutzt den globalen Geltungsbereich nicht.
  3. Sie erhalten mühelos einfach zu verwendende Variablen, auf die Sie weitere Logik aufbauen können.
  4. Die Anzahl der Bindestriche vor params spielt keine Rolle ( --all ist gleich -all ist gleich all=all )

Das nachstehende Skript ist eine Arbeitsdemonstration durch Kopieren und Einfügen. Siehe show_use Funktion zu verstehen, wie man parse_params .

Beschränkungen:

  1. Unterstützt keine durch Leerzeichen abgegrenzten Parameter ( -d 1 )
  2. Param-Namen verlieren die Bindestriche, so dass --any-param y -anyparam gleichwertig sind
  3. eval $(parse_params "$@") muss innerhalb von bash verwendet werden Funktion (es wird nicht im globalen Bereich funktionieren)

#!/bin/bash

# Universal Bash parameter parsing
# Parse equal sign separated params into named local variables
# Standalone named parameter value will equal its param name (--force creates variable $force=="force")
# Parses multi-valued named params into an array (--path=path1 --path=path2 creates ${path[*]} array)
# Puts un-named params as-is into ${ARGV[*]} array
# Additionally puts all named params as-is into ${ARGN[*]} array
# Additionally puts all standalone "option" params as-is into ${ARGO[*]} array
# @author Oleksii Chekulaiev
# @version v1.4.1 (Jul-27-2018)
parse_params ()
{
    local existing_named
    local ARGV=() # un-named params
    local ARGN=() # named params
    local ARGO=() # options (--params)
    echo "local ARGV=(); local ARGN=(); local ARGO=();"
    while [[ "$1" != "" ]]; do
        # Escape asterisk to prevent bash asterisk expansion, and quotes to prevent string breakage
        _escaped=${1/\*/\'\"*\"\'}
        _escaped=${_escaped//\'/\\\'}
        _escaped=${_escaped//\"/\\\"}
        # If equals delimited named parameter
        nonspace="[^[:space:]]"
        if [[ "$1" =~ ^${nonspace}${nonspace}*=..* ]]; then
            # Add to named parameters array
            echo "ARGN+=('$_escaped');"
            # key is part before first =
            local _key=$(echo "$1" | cut -d = -f 1)
            # Just add as non-named when key is empty or contains space
            if [[ "$_key" == "" || "$_key" =~ " " ]]; then
                echo "ARGV+=('$_escaped');"
                shift
                continue
            fi
            # val is everything after key and = (protect from param==value error)
            local _val="${1/$_key=}"
            # remove dashes from key name
            _key=${_key//\-}
            # skip when key is empty
            # search for existing parameter name
            if (echo "$existing_named" | grep "\b$_key\b" >/dev/null); then
                # if name already exists then it's a multi-value named parameter
                # re-declare it as an array if needed
                if ! (declare -p _key 2> /dev/null | grep -q 'declare \-a'); then
                    echo "$_key=(\"\$$_key\");"
                fi
                # append new value
                echo "$_key+=('$_val');"
            else
                # single-value named parameter
                echo "local $_key='$_val';"
                existing_named=" $_key"
            fi
        # If standalone named parameter
        elif [[ "$1" =~ ^\-${nonspace}+ ]]; then
            # remove dashes
            local _key=${1//\-}
            # Just add as non-named when key is empty or contains space
            if [[ "$_key" == "" || "$_key" =~ " " ]]; then
                echo "ARGV+=('$_escaped');"
                shift
                continue
            fi
            # Add to options array
            echo "ARGO+=('$_escaped');"
            echo "local $_key=\"$_key\";"
        # non-named parameter
        else
            # Escape asterisk to prevent bash asterisk expansion
            _escaped=${1/\*/\'\"*\"\'}
            echo "ARGV+=('$_escaped');"
        fi
        shift
    done
}

#--------------------------- DEMO OF THE USAGE -------------------------------

show_use ()
{
    eval $(parse_params "$@")
    # --
    echo "${ARGV[0]}" # print first unnamed param
    echo "${ARGV[1]}" # print second unnamed param
    echo "${ARGN[0]}" # print first named param
    echo "${ARG0[0]}" # print first option param (--force)
    echo "$anyparam"  # print --anyparam value
    echo "$k"         # print k=5 value
    echo "${multivalue[0]}" # print first value of multi-value
    echo "${multivalue[1]}" # print second value of multi-value
    [[ "$force" == "force" ]] && echo "\$force is set so let the force be with you"
}

show_use "param 1" --anyparam="my value" param2 k=5 --force --multi-value=test1 --multi-value=test2

0 Stimmen

Um die Demo zum Parsen von Parametern zu verwenden, die in Ihr Bash-Skript eingehen, führen Sie einfach aus show_use "$@"

0 Stimmen

Im Wesentlichen habe ich herausgefunden, dass github.com/renatosilva/easyoptions macht das Gleiche auf die gleiche Weise, ist aber etwas umfangreicher als diese Funktion.

11voto

vangorra Punkte 1601

Getopts funktioniert hervorragend, wenn Sie es #1 installiert haben und #2 beabsichtigen, es auf der gleichen Plattform auszuführen. OSX und Linux (zum Beispiel) verhalten sich in dieser Hinsicht unterschiedlich.

Hier ist eine (nicht getopts) Lösung, die Gleichheit, Nicht-Gleichheit und boolesche Flags unterstützt. Sie könnten Ihr Skript zum Beispiel auf diese Weise ausführen:

./script --arg1=value1 --arg2 value2 --shouldClean

# parse the arguments.
COUNTER=0
ARGS=("$@")
while [ $COUNTER -lt $# ]
do
    arg=${ARGS[$COUNTER]}
    let COUNTER=COUNTER+1
    nextArg=${ARGS[$COUNTER]}

    if [[ $skipNext -eq 1 ]]; then
        echo "Skipping"
        skipNext=0
        continue
    fi

    argKey=""
    argVal=""
    if [[ "$arg" =~ ^\- ]]; then
        # if the format is: -key=value
        if [[ "$arg" =~ \= ]]; then
            argVal=$(echo "$arg" | cut -d'=' -f2)
            argKey=$(echo "$arg" | cut -d'=' -f1)
            skipNext=0

        # if the format is: -key value
        elif [[ ! "$nextArg" =~ ^\- ]]; then
            argKey="$arg"
            argVal="$nextArg"
            skipNext=1

        # if the format is: -key (a boolean flag)
        elif [[ "$nextArg" =~ ^\- ]] || [[ -z "$nextArg" ]]; then
            argKey="$arg"
            argVal=""
            skipNext=0
        fi
    # if the format has not flag, just a value.
    else
        argKey=""
        argVal="$arg"
        skipNext=0
    fi

    case "$argKey" in 
        --source-scmurl)
            SOURCE_URL="$argVal"
        ;;
        --dest-scmurl)
            DEST_URL="$argVal"
        ;;
        --version-num)
            VERSION_NUM="$argVal"
        ;;
        -c|--clean)
            CLEAN_BEFORE_START="1"
        ;;
        -h|--help|-help|--h)
            showUsage
            exit
        ;;
    esac
done

9voto

Thanh Trung Punkte 3326

Ich möchte mein Projekt einreichen: https://github.com/flyingangel/argparser

source argparser.sh
parse_args "$@"

So einfach ist das. Die Umgebung wird mit Variablen mit dem gleichen Namen wie die Argumente gefüllt

7voto

CIsForCookies Punkte 10913

Basierend auf anderen Antworten hier, hier meine Version:

#!/bin/bash
set -e

function parse() {
    for arg in "$@"; do # transform long options to short ones
        shift
        case "$arg" in
            "--name") set -- "$@" "-n" ;;
            "--verbose") set -- "$@" "-v" ;;
            *) set -- "$@" "$arg"
        esac
    done

    while getopts "n:v" optname
    do
        case "$optname" in
            "n") name=${OPTARG} ;;
            "v") verbose=true ;;
        esac
    done
    shift "$((OPTIND-1))" # shift out all the already processed options
}

parse "$@"
echo "hello $name"
if [ ! -z $verbose ]; then echo 'nice to meet you!'; fi

Verwendung:

$ ./parse.sh
hello
$ ./parse.sh -n YOUR_NAME
hello YOUR_NAME
$ ./parse.sh -n YOUR_NAME -v
hello YOUR_NAME
nice to meet you!
$ ./parse.sh -v -n YOUR_NAME
hello YOUR_NAME
nice to meet you!
$ ./parse.sh -v
hello 
nice to meet you!

7voto

galmok Punkte 839

Ich möchte meine Version des Optionsparsing anbieten, die Folgendes ermöglicht:

-s p1
--stage p1
-w somefolder
--workfolder somefolder
-sw p1 somefolder
-e=hello

Auch dies ist möglich (könnte unerwünscht sein):

-s--workfolder p1 somefolder
-se=hello p1
-swe=hello p1 somefolder

Sie müssen vor der Verwendung entscheiden, ob = für eine Option verwendet werden soll oder nicht. Dies dient dazu, den Code sauber(er) zu halten.

while [[ $# > 0 ]]
do
    key="$1"
    while [[ ${key+x} ]]
    do
        case $key in
            -s*|--stage)
                STAGE="$2"
                shift # option has parameter
                ;;
            -w*|--workfolder)
                workfolder="$2"
                shift # option has parameter
                ;;
            -e=*)
                EXAMPLE="${key#*=}"
                break # option has been fully handled
                ;;
            *)
                # unknown option
                echo Unknown option: $key #1>&2
                exit 10 # either this: my preferred way to handle unknown options
                break # or this: do this to signal the option has been handled (if exit isn't used)
                ;;
        esac
        # prepare for next option in this key, if any
        [[ "$key" = -? || "$key" == --* ]] && unset key || key="${key/#-?/-}"
    done
    shift # option(s) fully processed, proceed to next input argument
done

1 Stimmen

Was bedeutet "+x" bei ${key+x}?

1 Stimmen

Es ist ein Test, um festzustellen, ob der "Schlüssel" vorhanden ist oder nicht. Weiter unten hebe ich den Schlüssel auf, wodurch die innere while-Schleife unterbrochen wird.

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