Zeg, ik heb een script dat wordt aangeroepen met deze regel:
./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile
of deze:
./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile
Wat is de geaccepteerde manier om dit zo te ontleden dat in elk geval (of een combinatie van beide) $v
, $f
en $d
worden allemaal ingesteld op true
en $outFile
is gelijk aan /fizz/someOtherFile
?
Antwoord 1, autoriteit 100%
Bash Space-Separated (bijv. --option argument
)
cat >/tmp/demo-space-separated.sh <<'EOF'
#!/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
EOF
chmod +x /tmp/demo-space-separated.sh
/tmp/demo-space-separated.sh -e conf -s /etc -l /usr/lib /etc/hosts
Uitvoer van kopieerpackering van het blok boven
FILE EXTENSION = conf
SEARCH PATH = /etc
LIBRARY PATH = /usr/lib
DEFAULT =
Number files in SEARCH PATH with EXTENSION: 14
Last line of file specified as non-opt/last argument:
#93.184.216.34 example.com
Gebruik
demo-space-separated.sh -e conf -s /etc -l /usr/lib /etc/hosts
bash is gelijk aan gescheiden (bijvoorbeeld --option=argument
)
cat >/tmp/demo-equals-separated.sh <<'EOF'
#!/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 "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
EOF
chmod +x /tmp/demo-equals-separated.sh
/tmp/demo-equals-separated.sh -e=conf -s=/etc -l=/usr/lib /etc/hosts
Uitvoer van het kopiëren en plakken van het bovenstaande blok
FILE EXTENSION = conf
SEARCH PATH = /etc
LIBRARY PATH = /usr/lib
DEFAULT =
Number files in SEARCH PATH with EXTENSION: 14
Last line of file specified as non-opt/last argument:
#93.184.216.34 example.com
Gebruik
demo-equals-separated.sh -e=conf -s=/etc -l=/usr/lib /etc/hosts
Om ${i#*=}
beter te begrijpen, zoek je naar “Substring Removal” in deze handleiding. Het is functioneel equivalent aan `sed 's/[^=]*=//' <<< "$i"`
die een onnodig subproces of `echo "$i" | sed 's/[^=]*=//'`
die tweeonnodige subprocessen aanroept.
Bash gebruiken met getopt[s]
getopt(1) beperkingen (oudere, relatief recente getopt
versies):
- kan geen argumenten aan die lege tekenreeksen zijn
- kan niet omgaan met argumenten met ingesloten witruimte
Recente getopt
-versies hebben deze beperkingen niet. Zie deze docsvoor meer informatie.
POSIX wint
Bovendien bieden de POSIX-shell en andere getopts
die deze beperkingen niet hebben. Ik heb een simplistisch getopts
voorbeeld toegevoegd.
cat >/tmp/demo-getopts.sh <<'EOF'
#!/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: $@"
EOF
chmod +x /tmp/demo-getopts.sh
/tmp/demo-getopts.sh -vf /etc/hosts foo bar
Uitvoer van het kopiëren en plakken van het bovenstaande blok
verbose=1, output_file='/etc/hosts', Leftovers: foo bar
Gebruik
demo-getopts.sh -vf /etc/hosts foo bar
De voordelen van getopts
zijn:
- Het is draagbaarder en werkt in andere shells zoals
dash
. - Het kan automatisch meerdere enkele opties aan, zoals
-vf filename
op de typische Unix-manier.
Het nadeel van getopts
is dat het alleen korte opties (-h
, niet --help
) aankan zonder aanvullende code.
Er is een getopts-tutorialwaarin wordt uitgelegd wat alle syntaxis en variabelen betekenen. In bash is er ook help getopts
, wat informatief kan zijn.
Antwoord 2, autoriteit 21%
Geen antwoord toont verbeterde getopt. En het antwoord met de meeste stemmenis misleidend:het negeert ofwel -vfd
stijl korte opties (gevraagd door het OP) of opties na positionele argumenten (ook gevraagd door het OP); en het negeert parseerfouten. In plaats daarvan:
- Gebruik verbeterde
getopt
van util-linux of voorheen GNU glibc.1 - Het werkt met
getopt_long()
de C-functie van GNU glibc. - geen enkele andere oplossing op deze pagina kan dit allemaal:
- verwerkt spaties, citeert tekens en zelfs binaire getallen in argumenten2(niet-verbeterde
getopt
kan dit niet) - het kan aan het einde opties aan:
script.sh -o outFile file1 file2 -v
(getopts
doet dit niet) - staat
=
-stijl lange opties toe:script.sh --outfile=fileOut --infile fileIn
(beide toestaan is lang als ze zichzelf parseren) - maakt gecombineerde korte opties mogelijk, b.v.
-vfd
(echt werk als het zelf parseert) - maakt het mogelijk om optie-argumenten aan te raken, bijv.
-oOutfile
of-vfdoOutfile
- verwerkt spaties, citeert tekens en zelfs binaire getallen in argumenten2(niet-verbeterde
- Is al zo oud3dat geen enkel GNU-systeem dit mist (bijvoorbeeld elke Linux heeft het).
- Je kunt het bestaan ervan testen met:
getopt --test
→ return value 4. - Andere
getopt
of shell-built-ingetopts
zijn van beperkt nut.
De volgende oproepen
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
allemaal terug
verbose: y, force: y, debug: y, in: ./foo/bar/someFile, out: /fizz/someOtherFile
met het volgende myscript
#!/bin/bash
# More safety, by turning some bugs into errors.
# Without `errexit` you don’t need ! and can replace
# PIPESTATUS with a simple $?, but I don’t do that.
set -o errexit -o pipefail -o noclobber -o nounset
# -allow a command to fail with !’s side effect on errexit
# -use return value from ${PIPESTATUS[0]}, because ! hosed $?
! 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
# -regarding ! and PIPESTATUS see above
# -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"
1verbeterde getopt is beschikbaar op de meeste “bash-systemen”, inclusief Cygwin; probeer op OS X brew install gnu-getoptof sudo port install getopt
2de POSIX exec()
-conventies hebben geen betrouwbare manier om binaire NULL door te geven in opdrachtregelargumenten; die bytes beëindigen het argument voortijdig
3eerste versie uitgebracht in 1997 of eerder (ik volgde het alleen terug naar 1997)
Antwoord 3, autoriteit 9%
deploy.sh
#!/bin/bash
while [[ "$#" -gt 0 ]]; do
case $1 in
-t|--target) target="$2"; shift ;;
-u|--uglify) uglify=1 ;;
*) echo "Unknown parameter passed: $1"; exit 1 ;;
esac
shift
done
echo "Where to deploy: $target"
echo "Should uglify : $uglify"
Gebruik:
./deploy.sh -t dev -u
# OR:
./deploy.sh --target dev --uglify
Antwoord 4, autoriteit 5%
Van digitalpeer.commet kleine aanpassingen:
Gebruik 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}
Om beter te begrijpen ${i#*=}
zoeken naar “substring verwijderen” in Deze gids . Het is functioneel equivalent aan `sed 's/[^=]*=//' <<< "$i"`
die een onnodige subprocess of `echo "$i" | sed 's/[^=]*=//'`
die twee noemt onnodige subprocessen.
5, Autoriteit 4%
getopt()
/ getopts()
is een goede optie. Gekopieerd van hier :
Het eenvoudige gebruik van “Getopt” wordt weergegeven in dit Mini-Script:
#!/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
Wat we hebben gezegd, is dat een van -een,
-B, -C of -D is toegestaan, maar dat -c wordt gevolgd door een argument (de “C:” zegt dat).Als we dit “g” noemen en het uitproberen:
bash-2.05a$ ./g -abc foo
Before getopt
-abc
foo
After getopt
-->-a
-->-b
-->-c
-->foo
-->--
We beginnen met twee argumenten, en
“Getopt” breekt de opties uit en
zet elk in zijn eigen argument. Het ook
toegevoegd “-“.
6, Autoriteit 4%
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
Deze oplossing:
- verwerkt
-n arg
en--name=arg
- laat argumenten aan het einde toe
- toont normale fouten als iets verkeerd is gespeld
- compatibel, gebruikt geen bashisms
- leesbaar, vereist geen status in een lus
Antwoord 7
Ik heb de eerdere antwoorden als uitgangspunt gebruikt om mijn oude adhoc param-parsing op te ruimen. Ik heb vervolgens de volgende sjablooncode gerefactored. Het behandelt zowel lange als korte parameters, waarbij = of spatiegescheiden argumenten worden gebruikt, evenals meerdere korte parameters die bij elkaar zijn gegroepeerd. Ten slotte voegt het alle niet-param-argumenten opnieuw in de $1,$2..-variabelen in.
#!/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
8
Ik heb ontdekt dat het schrijven van draagbare parsing in scripts zo frustrerend is dat ik Argbash– een FOSS-codegenerator die de code voor het parseren van argumenten voor uw script kan genereren, plus een aantal leuke functies:
Antwoord 9
# 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
Hierdoor kunt u zowel door spaties gescheiden opties/waarden als gelijk gedefinieerde waarden hebben.
U kunt uw script dus uitvoeren met:
./myscript --foo -b -o /fizz/file.txt
evenals:
./myscript -f --bar -o=/fizz/file.txt
en beide zouden hetzelfde eindresultaat moeten hebben.
PRO’S:
-
Staat zowel -arg=value als -arg value toe
-
Werkt met elke arg-naam die je in bash kunt gebruiken
- Betekenis -a of -arg of –arg of -a-r-g of wat dan ook
-
Puur bash. Het is niet nodig om getopt of getopts te leren/gebruiken
NADELEN:
-
Kan argumenten niet combineren
- Betekenis nee -abc. Je moet -a -b -c
. doen
Antwoord 10
Dit voorbeeld laat zien hoe u getopt
en eval
en HEREDOC
en shift
gebruikt om korte en lange parameters te verwerken met en zonder een vereiste waarde die volgt. Ook de switch/case-verklaring is beknopt en gemakkelijk te volgen.
#!/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
De belangrijkste regels van het bovenstaande script zijn deze:
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, to the point, leesbaar en regelt zo ongeveer alles (IMHO).
Ik hoop dat het iemand helpt.
Antwoord 11
Als u scripts maakt die uitwisselbaar zijn met andere hulpprogramma’s, kan onderstaande flexibiliteit nuttig zijn.
Ofwel:
command -x=myfilename.ext --another_switch
Of:
command -x myfilename.ext --another_switch
Hier is de code:
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
Antwoord 12
Voortbouwend op het antwoord van @bruno-bronosky, heb ik een “preprocessor” toegevoegd om een aantal veelvoorkomende opmaak te verwerken:
- Breidt
--longopt=val
uit tot--longopt val
- Breidt
-xyz
uit tot-x -y -z
- Ondersteunt
--
om het einde van vlaggen aan te geven - Toont een fout voor onverwachte opties
- Compacte en gemakkelijk leesbare optieschakelaar
#!/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") ;;
-*) 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[@]}"
13
Ik denk dat deze eenvoudig genoeg is om te gebruiken:
#!/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
Aanvoersvoorbeeld:
./myscript -v -do /fizz/someOtherFile -f ./foo/bar/someFile
OPT:v
OPT:d
OPT:o OPTARG:/fizz/someOtherFile
OPT:f
ARG:./foo/bar/someFile
14
Getopts Works Great If # 1 Je hebt het geïnstalleerd en # 2 Je bent van plan het op hetzelfde platform te laten rennen. OSX en Linux (bijvoorbeeld) gedragen zich in dit opzicht anders.
Hier is een (niet-getopts) oplossing die gelijk is aan, niet-gelijken en Booleaanse vlaggen. U kunt bijvoorbeeld uw script op deze manier uitvoeren:
./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
Antwoord 15
Nog een andere optie-parser (generator)
Een elegante optie-parser voor shell-scripts (volledige ondersteuning voor alle POSIX-shells)
https://github.com/ko1nksm/getoptions(Update: v3.3.0 uitgebracht op 2021-05 -02)
getoptionsis een nieuwe optie-parser (generator) geschreven in POSIX-compatibel shellscript en uitgebracht in augustus 2020. Het is voor degenen die de POSIX / GNU-stijloptiesyntaxis in uw shell willen ondersteunen scripts.
De ondersteunde syntaxis zijn -a
, +a
, -abc
, -vvv
, -p VALUE
, -pVALUE
, --flag
, --no-flag
, --with-flag
, --without-flag
, --param VALUE
, --param=VALUE
, --option[=VALUE]
, --no-option
--
.
Het ondersteunt subcommando’s, validatie, verkorte opties en het automatisch genereren van hulp. En werkt met alle POSIX-shells (dash 0.5.4+, bash 2.03+, ksh88+, mksh R28+, zsh 3.1.9+, yash 2.29+, busybox ash 1.1.3+, etc).
#!/bin/sh
VERSION="0.1"
parser_definition() {
setup REST help:usage -- "Usage: example.sh [options]... [arguments]..." ''
msg -- 'Options:'
flag FLAG -f --flag -- "takes no arguments"
param PARAM -p --param -- "takes one argument"
option OPTION -o --option on:"default" -- "takes one optional argument"
disp :usage -h --help
disp VERSION --version
}
eval "$(getoptions parser_definition) exit 1"
echo "FLAG: $FLAG, PARAM: $PARAM, OPTION: $OPTION"
printf '%s\n' "$@" # rest arguments
Het parseert de volgende argumenten:
example.sh -f --flag -p VALUE --param VALUE -o --option -oVALUE --option=VALUE 1 2 3
En automatisch genereren van hulp.
$ example.sh --help
Usage: example.sh [options]... [arguments]...
Options:
-f, --flag takes no arguments
-p, --param PARAM takes one argument
-o, --option[=OPTION] takes one optional argument
-h, --help
--version
Het is ook een optie-parsergenerator, die de volgende eenvoudige optie-parseercode genereert. Als u de gegenereerde code gebruikt, heeft u geen getoptions
nodig. Behaal echte draagbaarheid en geen afhankelijkheid.
FLAG=''
PARAM=''
OPTION=''
REST=''
getoptions_parse() {
OPTIND=$(($#+1))
while OPTARG= && [ $# -gt 0 ]; do
case $1 in
--?*=*) OPTARG=$1; shift
eval 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"' ${1+'"$@"'}
;;
--no-*|--without-*) unset OPTARG ;;
-[po]?*) OPTARG=$1; shift
eval 'set -- "${OPTARG%"${OPTARG#??}"}" "${OPTARG#??}"' ${1+'"$@"'}
;;
-[fh]?*) OPTARG=$1; shift
eval 'set -- "${OPTARG%"${OPTARG#??}"}" -"${OPTARG#??}"' ${1+'"$@"'}
OPTARG= ;;
esac
case $1 in
'-f'|'--flag')
[ "${OPTARG:- }" ] && OPTARG=${OPTARG#*\=} && set "noarg" "$1" && break
eval '[ ${OPTARG+x} ] &&:' && OPTARG='1' || OPTARG=''
FLAG="$OPTARG"
;;
'-p'|'--param')
[ $# -le 1 ] && set "required" "$1" && break
OPTARG=$2
PARAM="$OPTARG"
shift ;;
'-o'|'--option')
set -- "$1" "$@"
[ ${OPTARG+x} ] && {
case $1 in --no-*|--without-*) set "noarg" "${1%%\=*}"; break; esac
[ "${OPTARG:- }" ] && { shift; OPTARG=$2; } || OPTARG='default'
} || OPTARG=''
OPTION="$OPTARG"
shift ;;
'-h'|'--help')
usage
exit 0 ;;
'--version')
echo "${VERSION}"
exit 0 ;;
--)
shift
while [ $# -gt 0 ]; do
REST="${REST} \"\${$(($OPTIND-$#))}\""
shift
done
break ;;
[-]?*) set "unknown" "$1"; break ;;
*)
REST="${REST} \"\${$(($OPTIND-$#))}\""
esac
shift
done
[ $# -eq 0 ] && { OPTIND=1; unset OPTARG; return 0; }
case $1 in
unknown) set "Unrecognized option: $2" "$@" ;;
noarg) set "Does not allow an argument: $2" "$@" ;;
required) set "Requires an argument: $2" "$@" ;;
pattern:*) set "Does not match the pattern (${1#*:}): $2" "$@" ;;
notcmd) set "Not a command: $2" "$@" ;;
*) set "Validation error ($1): $2" "$@"
esac
echo "$1" >&2
exit 1
}
usage() {
cat<<'GETOPTIONSHERE'
Usage: example.sh [options]... [arguments]...
Options:
-f, --flag takes no arguments
-p, --param PARAM takes one argument
-o, --option[=OPTION] takes one optional argument
-h, --help
--version
GETOPTIONSHERE
}
16
Ik wil mijn project indienen: https://github.com/flyingangel/argarer
source argparser.sh
parse_args "$@"
Eenvoudig als dat. De omgeving wordt gevuld met variabelen met dezelfde naam als de argumenten
17
Dit is hoe ik het in een functie doe om te voorkomen dat Getopts tegelijkertijd ergens hoger in stapel lopen:
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
...
}
18
Ik wil mijn versie van optie-parseren aanbieden, die het volgende mogelijk maakt:
-s p1
--stage p1
-w somefolder
--workfolder somefolder
-sw p1 somefolder
-e=hello
Hiermee kunnen dit ook (kan ongewenst kunnen zijn):
-s--workfolder p1 somefolder
-se=hello p1
-swe=hello p1 somefolder
U moet vóór gebruik beslissen of = voor een optie moet worden gebruikt of niet. Dit is om de code clean(ish) te houden.
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
Antwoord 19
Er zijn verschillende manieren om cmdline-args te ontleden (bijv. GNU getopt (niet draagbaar) versus BSD (MacOS) getopt versus getopts) – allemaal problematisch. Deze oplossing
- is draagbaar!
- heeft nul afhankelijkheden, vertrouwt alleen op bash ingebouwde-ins
- maakt zowel korte als lange opties mogelijk
- verwerkt witruimte of tegelijkertijd het gebruik van
=
scheidingsteken tussen optie en argument - ondersteunt aaneengeschakelde korte optiestijl
-vxf
- handvat optie met optionele argumenten (bijv.
--color
vs--color=always
), - detecteert en rapporteert onbekende opties correct
- ondersteunt
--
om het einde van opties aan te geven, en - vereist geen opgeblazen code in vergelijking met alternatieven voor dezelfde functieset. D.w.z. beknopt en daardoor makkelijker te onderhouden
Voorbeelden: elk van
# flag
-f
--foo
# option with required argument
-b"Hello World"
-b "Hello World"
--bar "Hello World"
--bar="Hello World"
# option with optional argument
--baz
--baz="Optional Hello"
#!/usr/bin/env bash
usage() {
cat - >&2 <<EOF
NAME
program-name.sh - Brief description
SYNOPSIS
program-name.sh [-h|--help]
program-name.sh [-f|--foo]
[-b|--bar <arg>]
[--baz[=<arg>]]
[--]
FILE ...
REQUIRED ARGUMENTS
FILE ...
input files
OPTIONS
-h, --help
Prints this and exits
-f, --foo
A flag option
-b, --bar <arg>
Option requiring an argument <arg>
--baz[=<arg>]
Option that has an optional argument <arg>. If <arg>
is not specified, defaults to 'DEFAULT'
--
Specify end of options; useful if the first non option
argument starts with a hyphen
EOF
}
fatal() {
for i; do
echo -e "${i}" >&2
done
exit 1
}
# For long option processing
next_arg() {
if [[ $OPTARG == *=* ]]; then
# for cases like '--opt=arg'
OPTARG="${OPTARG#*=}"
else
# for cases like '--opt arg'
OPTARG="${args[$OPTIND]}"
OPTIND=$((OPTIND + 1))
fi
}
# ':' means preceding option character expects one argument, except
# first ':' which make getopts run in silent mode. We handle errors with
# wildcard case catch. Long options are considered as the '-' character
optspec=":hfb:-:"
args=("" "$@") # dummy first element so $1 and $args[1] are aligned
while getopts "$optspec" optchar; do
case "$optchar" in
h) usage; exit 0 ;;
f) foo=1 ;;
b) bar="$OPTARG" ;;
-) # long option processing
case "$OPTARG" in
help)
usage; exit 0 ;;
foo)
foo=1 ;;
bar|bar=*) next_arg
bar="$OPTARG" ;;
baz)
baz=DEFAULT ;;
baz=*) next_arg
baz="$OPTARG" ;;
-) break ;;
*) fatal "Unknown option '--${OPTARG}'" "see '${0} --help' for usage" ;;
esac
;;
*) fatal "Unknown option: '-${OPTARG}'" "See '${0} --help' for usage" ;;
esac
done
shift $((OPTIND-1))
if [ "$#" -eq 0 ]; then
fatal "Expected at least one required argument FILE" \
"See '${0} --help' for usage"
fi
echo "foo=$foo, bar=$bar, baz=$baz, files=${@}"
Antwoord 20
Oplossing die onverwerkte argumenten bewaart. Demo’s inbegrepen.
Hier is mijn oplossing. Het is ZEER flexibel en zou, in tegenstelling tot andere, geen externe pakketten moeten vereisen en overgebleven argumenten netjes afhandelen.
Gebruik is: ./myscript -flag flagvariable -otherflag flagvar2
Het enige wat u hoeft te doen is de validflags-regel bewerken. Het voegt een koppelteken toe en doorzoekt alle argumenten. Het definieert dan het volgende argument als de vlagnaam, bijvoorbeeld
./myscript -flag flagvariable -otherflag flagvar2
echo $flag $otherflag
flagvariable flagvar2
De hoofdcode (korte versie, uitgebreid met voorbeelden verder naar beneden, ook een versie met erroring out):
#!/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
De uitgebreide versie met ingebouwde ECHO-demo’s:
#!/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
Finale, deze fouten eruit als een ongeldig -argument wordt doorgegeven.
#!/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
Pluspunten: wat het doet, werkt heel goed. Het behoudt ongebruikte argumenten die veel van de andere oplossingen hier niet doen. Het maakt het ook mogelijk om variabelen aan te roepen zonder handmatig in het script te worden gedefinieerd. Het staat ook pre-populatie van variabelen toe als er geen corresponderend argument wordt gegeven. (Zie uitgebreid voorbeeld).
Nadelen: kan geen enkele complexe arg-tekenreeks ontleden, b.v. -xcvf zou worden verwerkt als een enkel argument. Je zou enigszins gemakkelijk extra code in de mijne kunnen schrijven die deze functionaliteit toevoegt.
Antwoord 21
Merk op dat getopt(1)
een kortstondige fout van AT&T was.
getopt is gemaakt in 1984 maar al begraven in 1986 omdat het niet echt bruikbaar was.
Een bewijs voor het feit dat getopt
erg verouderd is, is dat de getopt(1)
man-pagina nog steeds "$*"
vermeldt van "$@"
, dat in 1986 aan de Bourne Shell werd toegevoegd samen met de getopts(1)
shell die ingebouwd was om argumenten met spaties binnenin om te gaan.
BTW: als je geïnteresseerd bent in het ontleden van lange opties in shellscripts, kan het interessant zijn om te weten dat de getopt(3)
-implementatie van libc (Solaris) en ksh93
beide hebben een uniforme implementatie van lange opties toegevoegd die lange opties ondersteunt als aliassen voor korte opties. Dit zorgt ervoor dat ksh93
en de Bourne Shell
een uniforme interface voor lange opties implementeren via getopts
.
Een voorbeeld voor lange opties uit de Bourne Shell man-pagina:
getopts "f:(file)(input-file)o:(output-file)" OPTX "$@"
laat zien hoe lang optie-aliassen mogen worden gebruikt in zowel Bourne Shell als ksh93.
Zie de man-pagina van een recente Bourne Shell:
http://schillix.sourceforge.net/man/man1/bosh. 1.html
en de man-pagina voor getopt(3) van OpenSolaris:
http://schillix.sourceforge.net/man/man3c/getopt. 3c.html
en als laatste, de getopt(1) man-pagina om de verouderde $*:
. te verifiëren
http://schillix.sourceforge.net/man/man1/getopt. 1.html
Antwoord 22
Ik werd geïnspireerd door het relatief eenvoudige antwoord van @bronsonen kwam in de verleiding om het te verbeteren (zonder teveel toe te voegen) complexiteit). Dit is het resultaat:
Nog een Shell Argument Parser (ASAP) – POSIX, geen getopt*
- Gebruik een van de
-n [arg]
,-abn [arg]
,--name [arg]
en--name=arg
stijlen van opties; - Argumenten kunnen in elke volgorde voorkomen, alleen positieve argumenten blijven achter in
$@
na de lus; - Gebruik
--
om te forcerende resterende argumenten als positioneel te worden behandeld; - Detecteert ongeldige opties en ontbrekende argumenten;
- Hangt niet af van
getopt(s)
of externe tools (een functie gebruikt een eenvoudigsed
-commando); - Draagbaar, compact, goed leesbaar, met onafhankelijke functies.
# Convenience functions.
usage_error () { echo >&2 "$(basename $0): $1"; exit 2; }
assert_argument () { test "$1" != "$EOL" || usage_error "$2 requires an argument"; }
# One loop, nothing more.
EOL=$(echo '\01\03\03\07')
if [ "$#" != 0 ]; then
set -- "$@" "$EOL"
while [ "$1" != "$EOL" ]; do
opt="$1"; shift
case "$opt" in
# Your options go here.
-f|--flag) flag=true;;
-n|--name) assert_argument "$1" $opt; name="$1"; shift;;
-|''|[^-]*) set -- "$@" "$opt";; # positional argument, rotate to the end
# Extra features (you may remove any line you don't need):
--*=*) set -- "${opt%%=*}" "${opt#*=}" "$@";; # convert '--name=arg' to '--name' 'arg'
-[^-]?*) set -- $(echo "${opt#- }" | sed 's/\(.\)/ -\1/g') "$@";; # convert '-abc' to '-a' '-b' '-c'
--) while [ "$1" != "$EOL" ]; do set -- "$@" "$1"; shift; done;; # process remaining arguments as positional
-*) usage_error "unknown option: '$opt'";; # catch misspelled options
*) usage_error "this should NEVER happen ($opt)";; # sanity test for previous patterns
esac
done
shift # $EOL
fi
# Do something cool with "$@"... \o/
Opmerking:Ik weet het… Een argument met het binaire patroon0x01030307
zou de logica kunnen breken. Maar als iemand zo’n argument in een opdrachtregel gebruikt, verdienen ze het.
Antwoord 23
Positionele en op vlaggen gebaseerde argumenten mengen
–param=arg (is gelijk aan gescheiden)
Vlaggen vrijelijk mengen tussen positionele argumenten:
./script.sh dumbo 127.0.0.1 --environment=production -q -d
./script.sh dumbo --environment=production 127.0.0.1 --quiet -d
kan worden bereikt met een vrij beknopte aanpak:
# 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 (door spaties gescheiden)
Het is meestal duidelijker om de stijlen --flag=value
en --flag value
niet te mixen.
./script.sh dumbo 127.0.0.1 --environment production -q -d
Dit is een beetje lastig om te lezen, maar is nog steeds geldig
./script.sh dumbo --environment production 127.0.0.1 --quiet -d
Bron
# 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
Antwoord 24
Ik heb een bash-helper geschreven om een mooie bash-tool te schrijven
startpagina project: https://gitlab.mbedsys.org/mbedsys/bashopts
voorbeeld:
#!/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
geeft hulp:
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:"")
Geniet 🙂
25
Hier is mijn aanpak – met behulp van Regexp.
- Geen gettopts
- Het behandelt blok van korte parameters
-qwerty
- Het behandelt korte parameters
-q -w -e
- Het behandelt lange opties
--qwerty
- U kunt het kenmerk van korte of lange of lange optie doorgeven (als u een blok korte opties gebruikt, is het kenmerk bij de laatste optie bevestigd)
- U kunt spaties of
=
gebruiken om attributen te verschaffen, maar attribuutwedstrijden totdat u koppelt + spatie “scheidingsteken”, dus in--q=qwe ty
qwe ty
is een attribuut - het verwerkt een mix van al het bovenstaande, dus
-o a -op attr ibute --option=att ribu te --op-tion attribute --option att-ribute
is geldig
script:
#!/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
Antwoord 26
Stel dat we een shellscript maken met de naam test_args.sh
als volgt
#!/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"
Nadat we de volgende opdracht hebben uitgevoerd:
sh test_args.sh -year 2017 -flag -month 12 -day 22
De uitvoer zou zijn:
year=2017 month=12 day=22 flag=true
Antwoord 27
Ik wilde delen wat ik heb gemaakt voor het parseren van opties.
Sommige van mijn behoeften werden niet vervuld door de antwoorden hier, dus moest ik dit bedenken: https://github. com/MihirLuthra/bash_option_parser
Dit ondersteunt:
- Ontleding van subopties
- Aliasnamen voor opties
- Optionele argumenten
- Variabele argumenten
- Afdrukgebruik en fouten
Stel dat we een commando hebben met de naam fruit
met het volgende gebruik:
fruit <fruit-name> ...
[-e|—-eat|—-chew]
[-c|--cut <how> <why>]
<command> [<args>]
-e
maakt geen ruzie
-c
heeft twee argumenten, namelijk hoe te knippen en waarom te knippen
fruit
zelf heeft minstens één argument.
<command>
is voor subopties zoals apple
, orange
etc. (vergelijkbaar met git
die subopties heeft commit
, push
enz. )
Dus om het te ontleden:
parse_options \
'fruit' '1 ...' \
'-e' , '--eat' , '--chew' '0' \
'-c' , '--cut' '1 1' \
'apple' 'S' \
'orange' 'S' \
';' \
"$@"
Als er een gebruiksfout is opgetreden, kan deze als volgt worden afgedrukt met option_parser_error_msg
:
retval=$?
if [ $retval -ne 0 ]; then
# this will manage error messages if
# insufficient or extra args are supplied
option_parser_error_msg "$retval"
# This will print the usage
print_usage 'fruit'
exit 1
fi
Om nu te controleren of sommige opties zijn doorgegeven,
if [ -n "${OPTIONS[-c]}" ]
then
echo "-c was passed"
# args can be accessed in a 2D-array-like format
echo "Arg1 to -c = ${ARGS[-c,0]}"
echo "Arg2 to -c = ${ARGS[-c,1]}"
fi
Het parseren van subopties kan ook worden gedaan door $shift_count
door te geven aan parse_options_detailed
, waardoor het begint te parsen nadat de argumenten zijn verschoven om de argumenten van de suboptie te bereiken. Het wordt gedemonstreerd in dit voorbeeld.
Een gedetailleerde beschrijving vindt u in het leesmij-bestand en voorbeelden
in de repository.
Antwoord 28
Gebruik module “argumenten” van bash-modules
Voorbeeld:
#!/bin/bash
. import.sh log arguments
NAME="world"
parse_arguments "-n|--name)NAME;S" -- "$@" || {
error "Cannot parse command line."
exit 1
}
info "Hello, $NAME!"
Antwoord 29
Hier is een Getopts die het parseren met minimale code behaalt en kunt u definiëren wat u in één geval wilt extraheren met behulp van EVY met substring.
In principe 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}"
}
Verkrijgt de variabelen als locals in plaats van globalen als de meeste antwoorden hier.
gebeld als:
myrsync ---sourceurl http://abc.def.g ---sourceuser myuser ...
De $ {K: 3} is in feite een substring om de eerste ---
van de sleutel te verwijderen.