Hvordan kan jeg analysere kommandolinjeargumenter i Bash du?

stemmer
1k

Si: Jeg har et skript som blir kalt med denne linjen:

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

eller denne:

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

Hva er akseptert måte med analyse dette slik at i hvert fall (eller en kombinasjon av de to) $v, $fog $dvil alle bli satt til true, og $outFilevil være lik /fizz/someOtherFile?

Publisert på 10/10/2008 klokken 16:57
kilden bruker
På andre språk...                            


32 svar

stemmer
102

getopt()/ getopts()Er et godt alternativ. Stjålet fra her :

Den enkle bruken av "getopt" vises i denne mini-skript:

#!/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

Det vi har sagt er at noen av -a, b, c eller d vil bli tillatt, men at -c er etterfulgt av et argument ( "c:", sier det).

Hvis vi kaller dette "g" og prøve det ut:

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

Vi starter med to argumenter, og "getopt" bryter hverandre alternativene og setter hver sin argumentasjon. Det er også lagt til "-".

Svarte 10/10/2008 kl. 17:03
kilden bruker

stemmer
12

Jeg tror dette er enkelt nok å bruke:

#!/bin/bash
#

readopt='getopts $opts opt;rc=$?;[ $rc$opt == 0? ]&&exit 1;[ $rc == 0 ]||{ shift $[OPTIND-1];false; }'

opts=vfdo:

# Enumerating options
while eval $readopt
do
    echo OPT:$opt ${OPTARG+OPTARG:$OPTARG}
done

# Enumerating arguments
for arg
do
    echo ARG:$arg
done

Påkallelse eksempel:

./myscript -v -do /fizz/someOtherFile -f ./foo/bar/someFile
OPT:v 
OPT:d 
OPT:o OPTARG:/fizz/someOtherFile
OPT:f 
ARG:./foo/bar/someFile
Svarte 01/03/2012 kl. 15:15
kilden bruker

stemmer
107

fra: digitalpeer.com med mindre modifikasjoner

bruk myscript.sh -p=my_prefix -s=dirname -l=libname

#!/bin/bash
for i in "$@"
do
case $i in
    -p=*|--prefix=*)
    PREFIX="${i#*=}"

    ;;
    -s=*|--searchpath=*)
    SEARCHPATH="${i#*=}"
    ;;
    -l=*|--lib=*)
    DIR="${i#*=}"
    ;;
    --default)
    DEFAULT=YES
    ;;
    *)
            # unknown option
    ;;
esac
done
echo PREFIX = ${PREFIX}
echo SEARCH PATH = ${SEARCHPATH}
echo DIRS = ${DIR}
echo DEFAULT = ${DEFAULT}

For bedre å forstå ${i#*=}søk etter "Streng fjerning" i denne veiledningen . Det er funksjonelt ekvivalent til `sed 's/[^=]*=//' <<< "$i"`hvilke anrop en unødvendig delprosess eller `echo "$i" | sed 's/[^=]*=//'`som krever to unødvendig delprosesser.

Svarte 13/11/2012 kl. 10:31
kilden bruker

stemmer
1k

Metode 1: Bruke bash uten getopt [s]

To vanlige måter å passere nøkkelverdi-par argumenter er:

Bash adskilt av mellomrom (for eksempel --option argument) (uten getopt [s])

bruk ./myscript.sh -e conf -s /etc -l /usr/lib /etc/hosts

#!/bin/bash

POSITIONAL=()
while [[ $# -gt 0 ]]
do
key="$1"

case $key in
    -e|--extension)
    EXTENSION="$2"
    shift # past argument
    shift # past value
    ;;
    -s|--searchpath)
    SEARCHPATH="$2"
    shift # past argument
    shift # past value
    ;;
    -l|--lib)
    LIBPATH="$2"
    shift # past argument
    shift # past value
    ;;
    --default)
    DEFAULT=YES
    shift # past argument
    ;;
    *)    # unknown option
    POSITIONAL+=("$1") # save it in an array for later
    shift # past argument
    ;;
esac
done
set -- "${POSITIONAL[@]}" # restore positional parameters

echo FILE EXTENSION  = "${EXTENSION}"
echo SEARCH PATH     = "${SEARCHPATH}"
echo LIBRARY PATH    = "${LIBPATH}"
echo DEFAULT         = "${DEFAULT}"
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)
if [[ -n $1 ]]; then
    echo "Last line of file specified as non-opt/last argument:"
    tail -1 "$1"
fi

Bash equals-Separert (for eksempel --option=argument) (uten getopt [s])

bruk ./myscript.sh -e=conf -s=/etc -l=/usr/lib /etc/hosts

#!/bin/bash

for i in "$@"
do
case $i in
    -e=*|--extension=*)
    EXTENSION="${i#*=}"
    shift # past argument=value
    ;;
    -s=*|--searchpath=*)
    SEARCHPATH="${i#*=}"
    shift # past argument=value
    ;;
    -l=*|--lib=*)
    LIBPATH="${i#*=}"
    shift # past argument=value
    ;;
    --default)
    DEFAULT=YES
    shift # past argument with no value
    ;;
    *)
          # unknown option
    ;;
esac
done
echo "FILE EXTENSION  = ${EXTENSION}"
echo "SEARCH PATH     = ${SEARCHPATH}"
echo "LIBRARY PATH    = ${LIBPATH}"
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)
if [[ -n $1 ]]; then
    echo "Last line of file specified as non-opt/last argument:"
    tail -1 $1
fi

For bedre å forstå ${i#*=}søk etter "Streng fjerning" i denne veiledningen . Det er funksjonelt ekvivalent til `sed 's/[^=]*=//' <<< "$i"`hvilke anrop en unødvendig delprosess eller `echo "$i" | sed 's/[^=]*=//'`som krever to unødvendig delprosesser.

Metode 2: Bruke bash med getopt [s]

fra: http://mywiki.wooledge.org/BashFAQ/035#getopts

getopt (1) begrensninger (eldre, relativt-nyere getoptversjoner):

  • takler ikke argumenter som er tomme strenger
  • kan ikke håndtere argumenter med innebygd mellomrom

Nyere getoptversjoner har ikke disse begrensningene.

I tillegg POSIX skall (og andre) tilbud getoptssom ikke har disse begrensninger. Her er en forenklet getoptseksempel:

#!/bin/sh

# A POSIX variable
OPTIND=1         # Reset in case getopts has been used previously in the shell.

# Initialize our own variables:
output_file=""
verbose=0

while getopts "h?vf:" opt; do
    case "$opt" in
    h|\?)
        show_help
        exit 0
        ;;
    v)  verbose=1
        ;;
    f)  output_file=$OPTARG
        ;;
    esac
done

shift $((OPTIND-1))

[ "${1:-}" = "--" ] && shift

echo "verbose=$verbose, output_file='$output_file', Leftovers: $@"

# End of file

Fordelene med getoptser:

  1. Det er mer portable, og vil fungere i andre skjell som dash.
  2. Den kan håndtere flere enkle alternativer som -vf filenamei typisk Unix måten automatisk.

Ulempen med getoptser at det bare kan håndtere korte alternativer ( -hikke --help) uten ekstra kode.

Det er en getopts tutorial som forklarer hva alle de syntaks og variabler mener. I bash, er det også help getopts, som kan være informativ.

Svarte 07/01/2013 kl. 20:01
kilden bruker

stemmer
1

Bruk modulen "argumenter" fra bash-moduler

Eksempel:

#!/bin/bash
. import.sh log arguments

NAME="world"

parse_arguments "-n|--name)NAME;S" -- "$@" || {
  error "Cannot parse command line."
  exit 1
}

info "Hello, $NAME!"
Svarte 09/07/2013 kl. 16:51
kilden bruker

stemmer
6

Dette er hvordan jeg gjør i en funksjon for å unngå å bryte getopts kjøre på samme tid et høyere i bunken:

function waitForWeb () {
   local OPTIND=1 OPTARG OPTION
   local host=localhost port=8080 proto=http
   while getopts "h:p:r:" OPTION; do
      case "$OPTION" in
      h)
         host="$OPTARG"
         ;;
      p)
         port="$OPTARG"
         ;;
      r)
         proto="$OPTARG"
         ;;
      esac
   done
...
}
Svarte 19/07/2013 kl. 07:50
kilden bruker

stemmer
11

Utvidelse på utmerket svar av @guneysus, her er en tweak som lar brukeren bruke hvilken syntaks de foretrekker, for eksempel

command -x=myfilename.ext --another_switch 

vs

command -x myfilename.ext --another_switch

Det vil si likhets kan erstattes med mellomrom.

Denne "fuzzy tolkning" er kanskje ikke til din smak, men hvis du gjør skript som er utskiftbare med andre verktøy (som er tilfellet med mine, som skal arbeide med ffmpeg), er fleksibiliteten nyttig.

STD_IN=0

prefix=""
key=""
value=""
for keyValue in "$@"
do
  case "${prefix}${keyValue}" in
    -i=*|--input_filename=*)  key="-i";     value="${keyValue#*=}";; 
    -ss=*|--seek_from=*)      key="-ss";    value="${keyValue#*=}";;
    -t=*|--play_seconds=*)    key="-t";     value="${keyValue#*=}";;
    -|--stdin)                key="-";      value=1;;
    *)                                      value=$keyValue;;
  esac
  case $key in
    -i) MOVIE=$(resolveMovie "${value}");  prefix=""; key="";;
    -ss) SEEK_FROM="${value}";          prefix=""; key="";;
    -t)  PLAY_SECONDS="${value}";           prefix=""; key="";;
    -)   STD_IN=${value};                   prefix=""; key="";; 
    *)   prefix="${keyValue}=";;
  esac
done
Svarte 09/06/2014 kl. 13:46
kilden bruker

stemmer
1

Dette kan også være nyttig å vite, du kan sette en verdi, og hvis noen gir innspill, overstyre standard med den verdien ..

myscript.sh -f ./serverlist.txt eller bare ./myscript.sh (og det tar mislighold)

    #!/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}"
Svarte 14/06/2014 kl. 18:01
kilden bruker

stemmer
36

Jeg er ca 4 år for sent til dette spørsmålet, men ønsker å gi tilbake. Jeg brukte de tidligere svarene som et utgangspunkt for å rydde opp min gamle adhoc param parsing. Jeg så refactored ut følgende mal kode. Den håndterer både lange og korte parametere, ved hjelp = eller mellomrom argumenter, samt flere korte parametere gruppert sammen. Til slutt det re-setter alle ikke-param argumenter tilbake til $ 1, $ 2 .. variabler. Jeg håper det er nyttig.

#!/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
Svarte 01/07/2014 kl. 01:20
kilden bruker

stemmer
7

getopts fungerer bra hvis # 1 du har det installert og # 2 du har tenkt å kjøre den på samme plattform. OSX og Linux (for eksempel) oppfører seg annerledes i så måte.

Her er en (ikke getopts) løsning som støtter likemenn, ikke-likemenn, og boolske flagg. For eksempel kan du kjøre skriptet på denne måten:

./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
Svarte 12/02/2015 kl. 21:50
kilden bruker

stemmer
341

Ingen svar nevner forbedret getopt . Og topp var svaret er misvisende: Det ignorerer -⁠vfdstil korte alternativer (spurt av OP), opsjoner etter posisjon argumenter (også forespurt av OP) og det ignorerer parsing-feil. I stedet:

  • Bruk forbedret getoptfra util-linux eller tidligere GNU glibc . 1
  • Det fungerer med getopt_long()C funksjon av GNU glibc.
  • Har alle nyttige særtrekk (de andre ikke har dem):
    • håndterer mellomrom, siterer tegnene og selv binær i argumentene 2
    • det kan håndtere Alternativene på slutten: script.sh -o outFile file1 file2 -v
    • gjør =-stil lange flagg:script.sh --outfile=fileOut --infile fileIn
  • Er så gammel allerede tre at ingen GNU-systemet mangler dette (for eksempel noen Linux har det).
  • Du kan teste for sin eksistens med: getopt --test→ returverdi 4.
  • Andre getopteller shell-innbyggede getoptser av begrenset anvendelse.

Følgende anrop

myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile
myscript -v -f -d -o/fizz/someOtherFile -- ./foo/bar/someFile
myscript --verbose --force --debug ./foo/bar/someFile -o/fizz/someOtherFile
myscript --output=/fizz/someOtherFile ./foo/bar/someFile -vfd
myscript ./foo/bar/someFile -df -v --output /fizz/someOtherFile

alle tilbake

verbose: y, force: y, debug: y, in: ./foo/bar/someFile, out: /fizz/someOtherFile

med følgende myscript

#!/bin/bash
# saner programming env: these switches turn some bugs into errors
set -o errexit -o pipefail -o noclobber -o nounset

! getopt --test > /dev/null 
if [[ ${PIPESTATUS[0]} -ne 4 ]]; then
    echo "I’m sorry, `getopt --test` failed in this environment."
    exit 1
fi

OPTIONS=dfo:v
LONGOPTS=debug,force,output:,verbose

# -use ! and PIPESTATUS to get exit code with errexit set
# -temporarily store output to be able to check for errors
# -activate quoting/enhanced mode (e.g. by writing out “--options”)
# -pass arguments only via   -- "$@"   to separate them correctly
! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@")
if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
    # e.g. return value is 1
    #  then getopt has complained about wrong arguments to stdout
    exit 2
fi
# read getopt’s output this way to handle the quoting right:
eval set -- "$PARSED"

d=n f=n v=n outFile=-
# now enjoy the options in order and nicely split until we see --
while true; do
    case "$1" in
        -d|--debug)
            d=y
            shift
            ;;
        -f|--force)
            f=y
            shift
            ;;
        -v|--verbose)
            v=y
            shift
            ;;
        -o|--output)
            outFile="$2"
            shift 2
            ;;
        --)
            shift
            break
            ;;
        *)
            echo "Programming error"
            exit 3
            ;;
    esac
done

# handle non-option arguments
if [[ $# -ne 1 ]]; then
    echo "$0: A single input file is required."
    exit 4
fi

echo "verbose: $v, force: $f, debug: $d, in: $1, out: $outFile"

En forbedret getopt er tilgjengelig på de fleste “bash-systemer”, inkludert Cygwin; på OS X prøve brygge installere GNU-getopt ellersudo port install getopt
to POSIXexec()konvensjoner har ingen pålitelig måte å passere binær NULL i kommandolinjeargumenter; disse bytes for tidlig slutt på argumentet
tre første versjon gitt ut i 1997, eller før (I bare sporet den tilbake til 1997)

Svarte 20/04/2015 kl. 17:47
kilden bruker

stemmer
2

Blanding posisjon og flagg basert argumentasjon

--param = arg (er lik delt)

Fritt blande flagg mellom posisjon argumenter:

./script.sh dumbo 127.0.0.1 --environment=production -q -d
./script.sh dumbo --environment=production 127.0.0.1 --quiet -d

kan oppnås med en ganske kortfattet tilnærming:

# process flags
pointer=1
while [[ $pointer -le $# ]]; do
   param=${!pointer}
   if [[ $param != "-"* ]]; then ((pointer++)) # not a parameter flag so advance pointer
   else
      case $param in
         # paramter-flags with arguments
         -e=*|--environment=*) environment="${param#*=}";;
                  --another=*) another="${param#*=}";;

         # binary flags
         -q|--quiet) quiet=true;;
                 -d) debug=true;;
      esac

      # splice out pointer frame from positional list
      [[ $pointer -gt 1 ]] \
         && set -- ${@:1:((pointer - 1))} ${@:((pointer + 1)):$#} \
         || set -- ${@:((pointer + 1)):$#};
   fi
done

# positional remain
node_name=$1
ip_address=$2

--param arg (rommet som avgrenses)

Det er parantes klarere for ikke blande --flag=valueog --flag valuestiler.

./script.sh dumbo 127.0.0.1 --environment production -q -d

Dette er litt risikabelt å lese, men er fortsatt gyldig

./script.sh dumbo --environment production 127.0.0.1 --quiet -d

Kilde

# process flags
pointer=1
while [[ $pointer -le $# ]]; do
   if [[ ${!pointer} != "-"* ]]; then ((pointer++)) # not a parameter flag so advance pointer
   else
      param=${!pointer}
      ((pointer_plus = pointer + 1))
      slice_len=1

      case $param in
         # paramter-flags with arguments
         -e|--environment) environment=${!pointer_plus}; ((slice_len++));;
                --another) another=${!pointer_plus}; ((slice_len++));;

         # binary flags
         -q|--quiet) quiet=true;;
                 -d) debug=true;;
      esac

      # splice out pointer frame from positional list
      [[ $pointer -gt 1 ]] \
         && set -- ${@:1:((pointer - 1))} ${@:((pointer + $slice_len)):$#} \
         || set -- ${@:((pointer + $slice_len)):$#};
   fi
done

# positional remain
node_name=$1
ip_address=$2
Svarte 27/04/2015 kl. 02:42
kilden bruker

stemmer
4

Jeg vil gjerne gi min versjon av alternativ parsing, som gjør det mulig for følgende:

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

Også gir mulighet for dette (kan være uønsket):

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

Du må bestemme før bruk hvis = skal brukes på et alternativ eller ikke. Dette er å holde koden rene (ish).

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
Svarte 24/06/2015 kl. 10:54
kilden bruker

stemmer
64

Med fare for å legge til et annet eksempel å ignorere, her er min ordningen.

  • håndtak -n argog--name=arg
  • tillater argumenter ved enden
  • Utstillinger tilregnelig feil hvis noe er feilstavet
  • kompatibel, bruker ikke bashisms
  • lesbar, ikke krever opprettholdelse tilstand i en sløyfe

Håper det er nyttig for noen.

while [ "$#" -gt 0 ]; do
  case "$1" in
    -n) name="$2"; shift 2;;
    -p) pidfile="$2"; shift 2;;
    -l) logfile="$2"; shift 2;;

    --name=*) name="${1#*=}"; shift 1;;
    --pidfile=*) pidfile="${1#*=}"; shift 1;;
    --logfile=*) logfile="${1#*=}"; shift 1;;
    --name|--pidfile|--logfile) echo "$1 requires an argument" >&2; exit 1;;

    -*) echo "unknown option: $1" >&2; exit 1;;
    *) handle_argument "$1"; shift 1;;
  esac
done
Svarte 15/07/2015 kl. 23:43
kilden bruker

stemmer
1

Her er min forbedret løsning av Bruno Bronosky svar ved hjelp av variable arrays.

Det lar deg mikse parametere posisjon og gi deg en parameter rekke bevare rekkefølge uten opsjonene

#!/bin/bash

echo $@

PARAMS=()
SOFT=0
SKIP=()
for i in "$@"
do
case $i in
    -n=*|--skip=*)
    SKIP+=("${i#*=}")
    ;;
    -s|--soft)
    SOFT=1
    ;;
    *)
        # unknown option
        PARAMS+=("$i")
    ;;
esac
done
echo "SKIP            = ${SKIP[@]}"
echo "SOFT            = $SOFT"
    echo "Parameters:"
    echo ${PARAMS[@]}

Vil produksjonen for eksempel:

$ ./test.sh parameter -s somefile --skip=.c --skip=.obj
parameter -s somefile --skip=.c --skip=.obj
SKIP            = .c .obj
SOFT            = 1
Parameters:
parameter somefile
Svarte 06/10/2015 kl. 08:53
kilden bruker

stemmer
1

En annen løsning uten getopt [s], POSIX, gamle Unix stil

I likhet med løsningen Bruno Bronosky postet dette her er en uten bruk av getopt(s).

Hoved differensiere funksjon i min løsning er at den gjør det mulig å ha alternativer sammenkjedet sammen bare liker tar -xzf foo.tar.gzer lik tar -x -z -f foo.tar.gz. Og akkurat som i tar, psetc. ledende bindestrek er valgfritt for en blokk med korte alternativer (men dette kan endres lett). Lange flagg støttes også (men når en blokk starter med en da to ledende bindestreker er nødvendig).

Kode med eksempel alternativer

#!/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

For eksempel bruk kan du se eksempler nedenfor.

Plassering av alternativer med argumenter

For hva det er verdt det alternativer med argumentene ikke bli den siste (bare lange flagg må være). Så mens for eksempel i tar(i hvert fall i noen implementeringer) de falternativene må være siste fordi filnavnet slik ( tar xzf bar.tar.gzfungerer, men tar xfz bar.tar.gzikke gjør det) dette er ikke tilfelle her (se senere eksempler).

Flere alternativer med argumenter

Som en bonus opsjonsparametrene forbrukes i den rekkefølgen av alternativene av parametrene med nødvendige alternativer. Bare se på utgangen av manuset mitt her med kommandolinjen abc X Y Z(eller -abc X Y Z):

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

Lange flagg sammenkjedet samt

Også kan du også ha lange alternativer i alternativ blokk gitt at de opptrer sist i blokken. Så følgende kommandolinjer er alle ekvivalente (inkludert den rekkefølge i hvilken alternativer og argumenter blir behandlet):

  • -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

Alle disse føre til:

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

Ikke i denne løsningen

valg~~POS=TRUNC argumenter

Alternativer med valgfrie argumenter bør være mulig med litt arbeid, for eksempel ved å se fremover om det er en blokk uten bindestrek med; brukeren vil da trenge å sette en bindestrek foran hver blokk etter en blokk med en parameter som har en valgfri parameter. Kanskje dette er for komplisert å kommunisere med brukeren så bedre bare krever en ledende bindestrek helt i dette tilfellet.

Ting blir enda mer komplisert med flere mulige parametere. Jeg vil råde mot å gjøre alternativene prøver å være smart ved å bestemme hvorvidt et argument kan være for det eller ikke (for eksempel med et alternativ bare tar et tall som en valgfri argument) fordi dette kan bryte i fremtiden.

Jeg personlig foretrekker flere alternativer i stedet for valgfrie argumenter.

Alternativ argumenter introdusert med et likhetstegn

Akkurat som med valgfrie argumenter jeg ikke er en fan av dette (BTW, er det en tråd for å diskutere fordeler / ulemper med ulike parameter stiler?), Men hvis du vil at dette kan du sannsynligvis implementere det selv akkurat som gjøres på http: // mywiki.wooledge.org/BashFAQ/035#Manual_loop med en --long-with-arg=?*saken setningen og deretter stripping likhetstegnet (dette er Forresten den side som sier at foreta en parameter sammenkjeding er mulig med noen innsats, men "til venstre [det] som en øvelse for leseren "som gjorde meg ta dem på deres ord, men jeg startet fra scratch).

andre merknader

POSIX-kompatible, fungerer selv på gamle Busybox oppsett jeg hadde å forholde seg til (med f.eks cut, headog getoptsmangler).

Svarte 17/10/2015 kl. 21:17
kilden bruker

stemmer
3

Legg merke til at getopt(1)var en kort levende feil fra AT & T.

getopt ble opprettet i 1984, men allerede begravet i 1986 fordi det ikke var virkelig brukbart.

Et bevis for det faktum at getopter meget gammel, er at getopt(1)man side likevel nevner "$*"i stedet for "$@", som ble tilsatt til den Bourne Shell i 1986 sammen med getopts(1)skallet builtin for å håndtere argumenter med mellomrom inne.

BTW: Hvis du er interessert i analyse lange flagg i skallskript, kan det være av interesse å vite at getopt(3)gjennomføringen av libc (Solaris) og ksh93begge lagt en ensartet lang alternativ implementering som støtter lange flagg som aliaser for korte alternativer. Dette fører ksh93og Bourne Shelltil å gjennomføre en enhetlig grensesnitt for lange flagg via getopts.

Et eksempel for lange flagg tatt fra Bourne Shell mannen siden:

getopts "f:(file)(input-file)o:(output-file)" OPTX "$@"

viser hvor lang alternativ aliaser kan brukes i både Bourne Shell og ksh93.

Se mannen siden av en nylig Bourne Shell:

http://schillix.sourceforge.net/man/man1/bosh.1.html

og mannen siden for getopt (3) fra OpenSolaris:

http://schillix.sourceforge.net/man/man3c/getopt.3c.html

og sist, den getopt (1) man-siden for å kontrollere utdatert $ *:

http://schillix.sourceforge.net/man/man1/getopt.1.html

Svarte 19/10/2015 kl. 13:59
kilden bruker

stemmer
43

Mer konsis måte

script.sh

#!/bin/bash

while [[ "$#" > 0 ]]; do case $1 in
  -d|--deploy) deploy="$2"; shift;;
  -u|--uglify) uglify=1;;
  *) echo "Unknown parameter passed: $1"; exit 1;;
esac; shift; done

echo "Should deploy? $deploy"
echo "Should uglify? $uglify"

bruk:

./script.sh -d dev -u

# OR:

./script.sh --deploy dev --uglify
Svarte 20/11/2015 kl. 12:28
kilden bruker

stemmer
6

Jeg gir deg funksjonen parse_paramssom vil analysere params fra kommandolinjen.

  1. Det er en ren Bash løsning, ingen ekstra verktøy.
  2. Forurenser ikke globalt omfang.
  3. Uanstrengt returnerer du enkle å bruke variabler, som du kan bygge videre logikk på.
  4. Mengde streker før params spiller ingen rolle ( --alltilsvarer -allequals all=all)

Skriptet nedenfor er en copy-paste arbeids demonstrasjon. Se show_usefunksjon for å forstå hvordan du skal bruke parse_params.

begrensninger:

  1. Støtter ikke plass avgrensede params ( -d 1)
  2. Param navnene vil miste streker slik --any-paramog -anyparamer likeverdige
  3. eval $(parse_params "$@")må brukes inne bash funksjon (det vil ikke fungere i det globale omfang)

#!/bin/bash

# Universal Bash parameter parsing
# Parses equal sign separated params into local variables (--name=bob creates variable $name=="bob")
# 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)
# Parses un-named params into ${ARGV[*]} array
# Additionally puts all named params raw into ${ARGN[*]} array
# Additionally puts all standalone "option" params raw into ${ARGO[*]} array
# @author Oleksii Chekulaiev
# @version v1.4 (Jun-26-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
        _escaped=${1/\*/\'\"*\"\'}
        # If equals delimited named parameter
        if [[ "$1" =~ ^..*=..* ]]; then
            # Add to named parameters array
            echo "ARGN+=('$_escaped');"
            # key is part before first =
            local _key=$(echo "$1" | cut -d = -f 1)
            # 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
            if [[ "$_key" == "" ]]; then
                shift
                continue
            fi
            # 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" =~ ^\-. ]]; then
            # remove dashes
            local _key=${1//\-}
            # skip when key is empty
            if [[ "$_key" == "" ]]; then
                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
Svarte 01/07/2016 kl. 20:56
kilden bruker

stemmer
5

EasyOptions krever ingen analyse:

## Options:
##   --verbose, -v  Verbose mode
##   --output=FILE  Output filename

source easyoptions || exit

if test -n "${verbose}"; then
    echo "output file is ${output}"
    echo "${arguments[@]}"
fi
Svarte 04/07/2016 kl. 16:47
kilden bruker

stemmer
21

Jeg har funnet saken å skrive bærbar parsing i skript så frustrerende at jeg har skrevet Argbash - en FOSS kode generator som kan generere argumenter-analysekoden for skriptet pluss den har noen fine funksjoner:

https://argbash.io

Svarte 10/07/2016 kl. 22:40
kilden bruker

stemmer
1

Den øverste Svaret på dette spørsmålet virket litt buggy når jeg prøvde det - her er min løsning som jeg har funnet å være mer robust:

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
Svarte 08/08/2016 kl. 12:42
kilden bruker

stemmer
1

Løsning som bevarer ubehandlet argumenter. Demoer inkludert.

Her er min løsning. Det er svært fleksibel og i motsetning til andre, bør ikke kreve eksterne pakker og håndterer left argumenter rent.

Bruk er: ./myscript -flag flagvariable -otherflag flagvar2

Alt du trenger å gjøre er å redigere validflags linje. Det prepends en bindestrek og søker alle argumenter. Den definerer deretter neste argument som flagget navnet, for eksempel

./myscript -flag flagvariable -otherflag flagvar2
echo $flag $otherflag
flagvariable flagvar2

Hoved koden (avkortet, detaljert med eksempler lenger ned, også en versjon med erroring ut):

#!/usr/bin/env bash
#shebang.io
validflags="rate time number"
count=1
for arg in $@
do
    match=0
    argval=$1
    for flag in $validflags
    do
        sflag="-"$flag
        if [ "$argval" == "$sflag" ]
        then
            declare $flag=$2
            match=1
        fi
    done
        if [ "$match" == "1" ]
    then
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done
#Cleanup then restore the leftovers
shift $#
set -- $leftovers

Den ordrik versjon med innebygget ekko demoer:

#!/usr/bin/env bash
#shebang.io
rate=30
time=30
number=30
echo "all args
$@"
validflags="rate time number"
count=1
for arg in $@
do
    match=0
    argval=$1
#   argval=$(echo $@ | cut -d ' ' -f$count)
    for flag in $validflags
    do
            sflag="-"$flag
        if [ "$argval" == "$sflag" ]
        then
            declare $flag=$2
            match=1
        fi
    done
        if [ "$match" == "1" ]
    then
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done

#Cleanup then restore the leftovers
echo "pre final clear args:
$@"
shift $#
echo "post final clear args:
$@"
set -- $leftovers
echo "all post set args:
$@"
echo arg1: $1 arg2: $2

echo leftovers: $leftovers
echo rate $rate time $time number $number

Endelig en, er dette en feil ut om en ugyldig -argument er gått gjennom.

#!/usr/bin/env bash
#shebang.io
rate=30
time=30
number=30
validflags="rate time number"
count=1
for arg in $@
do
    argval=$1
    match=0
        if [ "${argval:0:1}" == "-" ]
    then
        for flag in $validflags
        do
                sflag="-"$flag
            if [ "$argval" == "$sflag" ]
            then
                declare $flag=$2
                match=1
            fi
        done
        if [ "$match" == "0" ]
        then
            echo "Bad argument: $argval"
            exit 1
        fi
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done
#Cleanup then restore the leftovers
shift $#
set -- $leftovers
echo rate $rate time $time number $number
echo leftovers: $leftovers

Pros: Hva den gjør, det håndterer svært godt. Det bevarer ubrukte argumenter som mange av de andre løsningene her ikke. Det gir også mulighet for variabler å bli kalt uten å bli definert for hånd i manuset. Den gjør det også forhåndsutfylling av variabler hvis ikke tilsvarende argument er gitt. (Se detaljert eksempel).

Ulemper: Kan ikke analysere en enkelt kompleks arg snor f.eks -xcvf ville behandle som et enkelt argument. Du kan noe lett skrive ekstra kode i gruven som legger denne funksjonaliteten skjønt.

Svarte 29/08/2016 kl. 03:44
kilden bruker

stemmer
1

Dette eksempelet viser hvordan man bruker getoptog evalog HEREDOCog shiftå håndtere korte og lange parametere med og uten en ønsket verdi som følger. Også bryteren / case uttalelsen er kortfattet og enkel å følge.

#!/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, don't 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

De vesentligste linjene i skriptet ovenfor er disse:

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

Kort, til poenget, lesbar, og håndterer omtrent alt (IMHO).

Håper det hjelper noen.

Svarte 07/09/2016 kl. 18:25
kilden bruker

stemmer
22

Mitt svar er i stor grad basert på svaret av Bruno Bronosky , men jeg moste liksom hans to ren bash implementeringer til en som jeg bruker ganske ofte.

# 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

Dette gjør det mulig å ha både plass atskilt alternativer / verdier, samt like definerte verdier.

Så du kan kjøre skriptet bruker:

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

i tillegg til:

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

og begge skal ha samme sluttresultat.

PROS:

  • Tillater både Arg = verdi og Arg verdi

  • Fungerer med alle arg navn som du kan bruke i bash

    • Betydning -a eller Arg eller --arg eller Arg eller hva
  • Pure bash. Du trenger ikke å lære / bruke getopt eller getopts

ULEMPER:

  • Kan ikke kombinere args

    • Betyr ingen -abc. Du må gjøre -a -b -c

Disse er de eneste profesjonelle / cons jeg kan tenke på på toppen av hodet mitt

Svarte 08/09/2016 kl. 18:59
kilden bruker

stemmer
1

Jeg har skrive et bash hjelper å skrive en hyggelig bash verktøy

Prosjektet hjem: https://gitlab.mbedsys.org/mbedsys/bashopts

eksempel:

#!/bin/bash -ei

# load the library
. bashopts.sh

# Enable backtrace dusplay on error
trap 'bashopts_exit_handle' ERR

# Initialize the library
bashopts_setup -n "$0" -d "This is myapp tool description displayed on help message" -s "$HOME/.config/myapprc"

# Declare the options
bashopts_declare -n first_name -l first -o f -d "First name" -t string -i -s -r
bashopts_declare -n last_name -l last -o l -d "Last name" -t string -i -s -r
bashopts_declare -n display_name -l display-name -t string -d "Display name" -e "\$first_name \$last_name"
bashopts_declare -n age -l number -d "Age" -t number
bashopts_declare -n email_list -t string -m add -l email -d "Email adress"

# Parse arguments
bashopts_parse_args "$@"

# Process argument
bashopts_process_args

vil gi hjelp:

NAME:
    ./example.sh - This is myapp tool description displayed on help message

USAGE:
    [options and commands] [-- [extra args]]

OPTIONS:
    -h,--help                          Display this help
    -n,--non-interactive true          Non interactive mode - [$bashopts_non_interactive] (type:boolean, default:false)
    -f,--first "John"                  First name - [$first_name] (type:string, default:"")
    -l,--last "Smith"                  Last name - [$last_name] (type:string, default:"")
    --display-name "John Smith"        Display name - [$display_name] (type:string, default:"$first_name $last_name")
    --number 0                         Age - [$age] (type:number, default:0)
    --email                            Email adress - [$email_list] (type:string, default:"")

Nyt :)

Svarte 20/02/2017 kl. 21:30
kilden bruker

stemmer
1

Her er min tilnærming - ved hjelp av regexp.

  • ingen getopts
  • den håndterer blokk av korte parametere -qwerty
  • den håndterer korte parametere -q -w -e
  • den håndterer lange flagg --qwerty
  • du kan passere attributt for kort eller lang alternativ (hvis du bruker blokk med korte alternativer, er attributt knyttet til det siste alternativet)
  • du kan bruke mellomrom eller =å gi attributter, men tilskriver kampene før møte bindestrek + space "skilletegn", så i --q=qwe ty qwe tyer en egenskap
  • den håndterer blanding av alle over så -o a -op attr ibute --option=att ribu te --op-tion attribute --option att-ributeer gyldig

manus:

#!/usr/bin/env sh

help_menu() {
  echo "Usage:

  ${0##*/} [-h][-l FILENAME][-d]

Options:

  -h, --help
    display this help and exit

  -l, --logfile=FILENAME
    filename

  -d, --debug
    enable debug
  "
}

parse_options() {
  case $opt in
    h|help)
      help_menu
      exit
     ;;
    l|logfile)
      logfile=${attr}
      ;;
    d|debug)
      debug=true
      ;;
    *)
      echo "Unknown option: ${opt}\nRun ${0##*/} -h for help.">&2
      exit 1
  esac
}
options=$@

until [ "$options" = "" ]; do
  if [[ $options =~ (^ *(--([a-zA-Z0-9-]+)|-([a-zA-Z0-9-]+))(( |=)(([\_\.\?\/\\a-zA-Z0-9]?[ -]?[\_\.\?a-zA-Z0-9]+)+))?(.*)|(.+)) ]]; then
    if [[ ${BASH_REMATCH[3]} ]]; then # for --option[=][attribute] or --option[=][attribute]
      opt=${BASH_REMATCH[3]}
      attr=${BASH_REMATCH[7]}
      options=${BASH_REMATCH[9]}
    elif [[ ${BASH_REMATCH[4]} ]]; then # for block options -qwert[=][attribute] or single short option -a[=][attribute]
      pile=${BASH_REMATCH[4]}
      while (( ${#pile} > 1 )); do
        opt=${pile:0:1}
        attr=""
        pile=${pile/${pile:0:1}/}
        parse_options
      done
      opt=$pile
      attr=${BASH_REMATCH[7]}
      options=${BASH_REMATCH[9]}
    else # leftovers that don't match
      opt=${BASH_REMATCH[10]}
      options=""
    fi
    parse_options
  fi
done
Svarte 15/03/2017 kl. 13:24
kilden bruker

stemmer
2

Anta vi skape et shell script som heter test_args.shsom følger

#!/bin/sh
until [ $# -eq 0 ]
do
  name=${1:1}; shift;
  if [[ -z "$1" || $1 == -* ]] ; then eval "export $name=true"; else eval "export $name=$1"; shift; fi  
done
echo "year=$year month=$month day=$day flag=$flag"

Etter kjører vi følgende kommando:

sh test_args.sh  -year 2017 -flag  -month 12 -day 22 

Utgangen vil være:

year=2017 month=12 day=22 flag=true
Svarte 10/10/2017 kl. 22:49
kilden bruker

stemmer
1

Jeg vil sende inn prosjektet mitt: https://github.com/flyingangel/argparser

source argparser.sh
parse_args "$@"

Så enkelt som det. Miljøet vil bli fylt med variabler med samme navn som argumenter

Svarte 16/09/2018 kl. 17:45
kilden bruker

stemmer
0

Enkel og lett å endre, kan parametrene være i hvilken som helst rekkefølge. denne kan modifiseres for å ta parametre i en hvilken som helst form (-a, -A, a, etc).

for arg in "$@"
do
   key=$(echo $arg | cut -f1 -d=)`
   value=$(echo $arg | cut -f2 -d=)`
   case "$key" in
        name|-name)      read_name=$value;;
        id|-id)          read_id=$value;;
        *)               echo "I dont know what to do with this"
   ease
done
Svarte 16/12/2018 kl. 07:53
kilden bruker

stemmer
0

Utvider på @ Bruno-bronosky svar, jeg har lagt en "preprosessor" for å håndtere noen felles formatering:

  • utvider --longopt=valinn--longopt val
  • utvider -xyzinn-x -y -z
  • Støtter --å angi slutten av flagg
  • Viser en feil for uventede alternativer
  • Kompakt og lett-å-lese alternativene slå
#!/bin/bash

# Report usage
usage() {
  echo "Usage:"
  echo "$(basename $0) [options] [--] [file1, ...]"

  # Optionally exit with a status code
  if [ -n "$1" ]; then
    exit "$1"
  fi
}

invalid() {
  echo "ERROR: Unrecognized argument: $1" >&2
  usage 1
}

# Pre-process options to:
# - expand -xyz into -x -y -z
# - expand --longopt=arg into --longopt arg
ARGV=()
END_OF_OPT=
while [[ $# -gt 0 ]]; do
  arg="$1"; shift
  case "${END_OF_OPT}${arg}" in
    --) ARGV+=("$arg"); END_OF_OPT=1 ;;
    --*=*)ARGV+=("${arg%%=*}" "${arg#*=}") ;;
    --*) ARGV+=("$arg"); END_OF_OPT=1 ;;
    -*) for i in $(seq 2 ${#arg}); do ARGV+=("-${arg:i-1:1}"); done ;;
    *) ARGV+=("$arg") ;;
  esac
done

# Apply pre-processed options
set -- "${ARGV[@]}"

# Parse options
END_OF_OPT=
POSITIONAL=()
while [[ $# -gt 0 ]]; do
  case "${END_OF_OPT}${1}" in
    -h|--help)      usage 0 ;;
    -p|--password)  shift; PASSWORD="$1" ;;
    -u|--username)  shift; USERNAME="$1" ;;
    -n|--name)      shift; names+=("$1") ;;
    -q|--quiet)     QUIET=1 ;;
    -C|--copy)      COPY=1 ;;
    -N|--notify)    NOTIFY=1 ;;
    --stdin)        READ_STDIN=1 ;;
    --)             END_OF_OPT=1 ;;
    -*)             invalid "$1" ;;
    *)              POSITIONAL+=("$1") ;;
  esac
  shift
done

# Restore positional parameters
set -- "${POSITIONAL[@]}"
Svarte 05/03/2019 kl. 17:12
kilden bruker

stemmer
1

Her er en getopts som oppnår analyser med minimal kode og lar deg definere hva du ønsker å pakke i ett tilfelle å bruke eval med understreng.

I utgangspunktet eval "local key='val'"

function myrsync() {

        local backup=("${@}") args=(); while [[ $# -gt 0 ]]; do k="$1";
                case "$k" in
                    ---sourceuser|---sourceurl|---targetuser|---targeturl|---file|---exclude|---include)
                        eval "local ${k:3}='${2}'"; shift; shift    # Past two arguments
                    ;;
                    *)  # Unknown option  
                        args+=("$1"); shift;                        # Past argument only
                    ;;                                              
                esac                                                
        done; set -- "${backup[@]}"                                 # Restore $@


        echo "${sourceurl}"
}

Erklærer variablene som lokalbefolkningen i stedet for globals som de fleste svarene her.

Kalt som:

myrsync ---sourceurl http://abc.def.g ---sourceuser myuser ... 

Den $ {k: 3} er i utgangspunktet en delstreng for å fjerne den første ---fra nøkkelen.

Svarte 01/12/2019 kl. 14:06
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more