Kaip analizuoti komandų eilutės argumentus „bash“?

Tarkime, turiu scenarijų, kuris vadinamas šia eilute:

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

arba šis:

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

Kas yra priimta šioje sintaksės analizėje, kad kiekvienu atveju (arba kai kurių dviejų derinys) $v , $f ir $d visi nustatytų true , o $outFile bus lygus /fizz/someOtherFile ?

1488
10 окт. „Lawrence Johnston“ nustatė spalio 10 d 2008-10-10 19:57 '08, 07:57 pm 2008-10-10 19:57
@ 30 atsakymų

1 metodas: bash naudojimas be getopt [s]

Du bendri būdai perduoti argumentus raktų vertės porai:

Bash Space -s yra susieta (pvz., --option argument ) (be getopt [s])

Naudojant ./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}"\'\"*\"\'} # 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 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 
8
01 июля '16 в 23:56 2016-07-01 23:56 atsakymas pateikiamas Oleksii Čekulajevui liepos 1 d. 16, 23:56 2016-07-01 23:56

„EasyOptions“ nereikia analizuoti:

 ## 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 
7
04 июля '16 в 19:47 2016-07-04 19:47 atsakymą pateikė Renato Silva , liepos 4 d. 16, 19:47 2016-07-04 19:47

Štai kaip aš tai atlieku funkcijoje, kad būtų išvengta sutrikimų, tuo pačiu metu kažkur aukščiau ant kamino:

 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 ... } 
6
19 июля '13 в 10:50 2013-07-19 10:50 atsakymą pateikė akostadinovas liepos 19 d. 13 val. 10:50 2013-07-19 10:50

Atkreipkite dėmesį, kad getopt(1) buvo trumpas tiesioginis klaidas iš AT T.

getopt buvo sukurtas 1984 m., bet jau palaidotas 1986 m., nes tai nebuvo tikrai naudinga.

Įrodymas, kad „ getopt labai pasenęs, yra tas, kad „man“ puslapio „ getopt(1) vietoj "$@" vis dar minimas "$*" "$@" , kuris buvo pridėtas prie „Bourne“ lukšto 1986 m. getopts(1) įmontuoti, kad būtų galima tvarkyti argumentus su tarpais.

BTW: jei jus domina ilgų parametrų analizavimas apvalkalo scenarijuose, gali būti įdomu žinoti, kad „ getopt(3) įgyvendinimas iš libc (Solaris) ir ksh93 pridėjo vieną ilgą parinktį, kuri palaiko ilgus parametrus kaip trumpus variantus kaip slapyvardžius. Dėl to „ ksh93 ir „ Bourne Shell ksh93 vieną sąsają ilgoms parinktims per „ getopts .

Ilgų parametrų pavyzdys, paimtas iš „Bourne Shell“ vadovo puslapio:

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

parodo, kiek laiko alternatyvūs slapyvardžiai gali būti naudojami tiek „Bourne Shell“, tiek „ksh93“.

Žiūrėkite naujausią gimimo nuorodos nuorodos puslapį:

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

ir nuorodos puslapis „getopt“ (3) iš „OpenSolaris“:

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

ir galiausiai „getopt“ (1) žmogaus puslapis, skirtas patikrinti pasenusius $ *:

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

4
19 окт. Atsakymas pateikiamas 19 val. 2015-10-19 16:59 '15, 16:59 pm 2015-10-19 16:59

Norėčiau pasiūlyti parametrų analizės versiją, kuri leidžia:

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

Taip pat leidžiama (gali būti nepageidaujama):

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

Prieš naudodami if =, turite nuspręsti, ar naudoti parinktį, ar ne. Tai reiškia, kad kodas yra švarus (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> 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 
4
24 июня '15 в 13:54 2015-06-24 13:54 atsakymą pateikė galmok , birželio 24 d. 15 d., 13.44 val. 2015-06-24 13:54

Tarkime, mes sukuriame scenarijų, pavadintą test_args.sh , taip

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

Atlikus šią komandą:

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

Rezultatai bus:

 year=2017 month=12 day=22 flag=true 
3
11 окт. Jono atsakymą pateikė spalio 11 d 2017-10-11 01:49 '17 at 1:49 2017-10-11 01:49

Sprendimas, taupantis neapdorotus argumentus. Įtraukti parodymai.

Čia yra mano sprendimas. Tai labai lanksti ir, skirtingai nuo kitų, neturėtų reikalauti išorinių paketų ir tvarkyti likusius argumentus.

Naudokite: ./myscript -flag flagvariable -otherflag flagvar2

Viskas, ką jums reikia padaryti, yra redaguoti galiojančią eilutę. Jis prideda brūkšnelį ir ieško visų argumentų. Tada jis apibrėžia kitą argumentą, pavyzdžiui, kaip vėliavos pavadinimą.

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

Pagrindinis kodas (trumpas variantas, pateikiamas toliau pateiktuose pavyzdžiuose, taip pat versija su klaida):

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

Verbalinė versija su įmontuotomis echo demonstracijomis:

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