说,我有一个脚本被此行调用:
./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile
或这一个:
./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile
什么是分析这使得在各种情况下的接受的方式(或两者的组合)$v
,$f
以及 $d
将全部设置为true
和$outFile
将等于/fizz/someOtherFile
?
说,我有一个脚本被此行调用:
./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile
或这一个:
./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile
什么是分析这使得在各种情况下的接受的方式(或两者的组合)$v
,$f
以及 $d
将全部设置为true
和$outFile
将等于/fizz/someOtherFile
?
Answers:
传递键值对参数的两种常见方法是:
--option argument
)(没有getopt [s])用法 demo-space-separated.sh -e conf -s /etc -l /usr/lib /etc/hosts
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
复制粘贴以上块的输出:
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
--option=argument
)(没有getopt [s])用法 demo-equals-separated.sh -e=conf -s=/etc -l=/usr/lib /etc/hosts
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
复制粘贴以上块的输出:
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
为了更好地理解${i#*=}
,在本指南中搜索“子字符串删除” 。它在功能上等同于`sed 's/[^=]*=//' <<< "$i"`
调用一个不必要的子流程或`echo "$i" | sed 's/[^=]*=//'`
调用两个不必要的子流程。
来自:http : //mywiki.wooledge.org/BashFAQ/035#getopts
getopt(1)限制(较旧的,相对较新的getopt
版本):
最新getopt
版本没有这些限制。
此外,POSIX shell(及其他)提供的getopts
功能没有这些限制。我包括了一个简单的getopts
例子。
用法 demo-getopts.sh -vf /etc/hosts foo bar
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
复制粘贴以上块的输出:
verbose=1, output_file='/etc/hosts', Leftovers: foo bar
的优点getopts
是:
dash
。 -vf filename
典型的Unix方式一样自动处理多个单个选项。缺点getopts
是它只能处理简短的选项(-h
,而不能--help
),而无需附加代码。
有一个getopts教程,它解释了所有语法和变量的含义。在bash中,还有help getopts
,可能会提供参考。
getopt
不是GNU实用程序,它是的一部分util-linux
。
–default
。在第一个示例中,我注意到if –default
是最后一个参数,除非while [[ $# -gt 1 ]]
设置为 while [[ $# -gt 0 ]]
没有答案提到增强的getopt。而答案,投票顶是一种误导:它要么忽略-vfd
风格期权短仓(由OP要求)或位置参数(也由OP的要求)后选择; 并且它忽略了解析错误。代替:
getopt
util-linux或以前的GNU glibc中的增强功能。1个getopt_long()
GNU glibc的C函数一起使用。getopt
功能不能这样做)script.sh -o outFile file1 file2 -v
(getopts
不这样做)=
-style长选项:(script.sh --outfile=fileOut --infile fileIn
如果自解析,则两者都很长)-vfd
(如果进行自我解析,则为实际工作)-oOutfile
或-vfdoOutfile
getopt --test
→返回值4。getopt
或Shell-builtin getopts
用途有限。以下电话
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
全部归还
verbose: y, force: y, debug: y, in: ./foo/bar/someFile, out: /fizz/someOtherFile
与以下 myscript
#!/bin/bash
# saner programming env: these switches turn some bugs into errors
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"
在大多数“ bash系统”(包括Cygwin)上都可以使用 1个增强的getopt。在OS X上,尝试执行 brew install gnu-getopt或sudo port install getopt
2,POSIXexec()
约定没有可靠的方法在命令行参数中传递二进制NULL。这些字节过早地结束了参数
3的第一个版本,发布于1997年或之前(我仅追溯到1997年)
getopt
则是一种方法。
getopt
是,它可能无法在包装脚本中方便地使用,在包装脚本中,可能没有几个特定于包装脚本的选项,然后将non-wrapper-script选项完整地传递给包装的可执行文件。假设我有一个grep
包装器,mygrep
并且有一个--foo
特定于的选项mygrep
,那么我就不能做mygrep --foo -A 2
,并且将其-A 2
自动传递给grep
;。我需要做mygrep --foo -- -A 2
。这是我在您的解决方案之上的实现。
man bash
script.sh
#!/bin/bash
while [[ "$#" -gt 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"
用法:
./script.sh -d dev -u
# OR:
./script.sh --deploy dev --uglify
while [[ "$#" > 1 ]]
,如果我想支持结束用布尔标志线./script.sh --debug dev --uglify fast --verbose
。例如:gist.github.com/hfossli/4368aa5a577742c3c9f9266ed214aa58
./script.sh -d dev -d prod
将导致deploy == 'prod'
。无论如何我都用过它:P :):+1:
./script.sh -d
不会产生错误,而只是设置$deploy
为空字符串。
来自:digitalpeer.com,进行了少量修改
用法 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}
为了更好地理解${i#*=}
,在本指南中搜索“子字符串删除” 。它在功能上等同于`sed 's/[^=]*=//' <<< "$i"`
调用一个不必要的子流程或`echo "$i" | sed 's/[^=]*=//'`
调用两个不必要的子流程。
mount -t tempfs ...
。人们可以通过像可能解决这一问题while [ $# -ge 1 ]; do param=$1; shift; case $param in; -p) prefix=$1; shift;;
等等
-vfd
样式组合的简短选项。
getopt()
/ getopts()
是一个不错的选择。从这里被盗:
这个迷你脚本显示了“ getopt”的简单用法:
#!/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
我们所说的是-a,-b,-c或-d中的任何一个都将被允许,但是-c后跟一个参数(“ c:”表示)。
如果我们称它为“ g”并尝试一下:
bash-2.05a$ ./g -abc foo
Before getopt
-abc
foo
After getopt
-->-a
-->-b
-->-c
-->foo
-->--
我们从两个参数开始,“ getopt”将选项分开,并将每个选项置于其自己的参数中。它还添加了“-”。
这是我的计划,冒着添加另一个示例忽略的风险。
-n arg
和--name=arg
希望对某人有用。
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
*) die "unrecognized argument: $1"
或者将args收集到一个变量中*) args+="$1"; shift 1;;
。
shift 2
,发出shift
两次代替shift 2
。建议编辑。
我这个问题迟到了4年,但想退回。我以较早的答案为起点来整理旧的即席参数解析。然后,我重构了以下模板代码。它使用=或以空格分隔的参数处理长参数和短参数,以及组合在一起的多个短参数。最后,它将所有非参数参数重新插入到$ 1,$ 2 ..变量中。我希望它有用。
#!/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
-c1
。而且使用=
来将短期权与他们的论点分开是不寻常的……
我发现在脚本中编写可移植的解析非常令人沮丧,以至于我写了Argbash -FOSS代码生成器,它可以为脚本生成参数解析代码,并且具有一些不错的功能:
我的答案主要基于Bruno Bronosky的答案,但是我将他的两个纯bash实现混搭为一个我经常使用的实现。
# 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
这使您可以同时使用空格分隔的选项/值以及相等的定义值。
因此,您可以使用以下命令运行脚本:
./myscript --foo -b -o /fizz/file.txt
以及:
./myscript -f --bar -o=/fizz/file.txt
两者的最终结果应该相同。
优点:
允许-arg = value和-arg值
与您可以在bash中使用的任何arg名称一起使用
纯扑 无需学习/使用getopt或getopts
缺点:
不能合并参数
这些是我能想到的唯一优点/缺点
我认为这个简单易用:
#!/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
调用示例:
./myscript -v -do /fizz/someOtherFile -f ./foo/bar/someFile
OPT:v
OPT:d
OPT:o OPTARG:/fizz/someOtherFile
OPT:f
ARG:./foo/bar/someFile
-a=1
用作argc样式。我更喜欢先放置主选项-options,然后放置特殊的单个间距-o option
。我正在寻找最简单的vs更好的方法来读取argvs。
./myscript -v -d fail -o /fizz/someOtherFile -f ./foo/bar/someFile
使用自己的脚本检查此行。-d选项未设置为d:
扩展@guneysus的出色答案,这是一项调整,使用户可以使用他们喜欢的任何语法,例如
command -x=myfilename.ext --another_switch
与
command -x myfilename.ext --another_switch
也就是说,等于可以用空格代替。
这种“模糊的解释”可能并不符合您的喜好,但是如果您要编写可与其他实用程序互换的脚本(像我的情况那样,必须与ffmpeg一起使用),那么灵活性就很有用。
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
本示例说明如何使用getopt
and eval
和HEREDOC
and shift
来处理带有和不带有必需值的短和长参数。同样,switch / case语句简洁明了,易于遵循。
#!/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
上面脚本中最重要的几行是:
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
简而言之,可读性强,几乎可以处理所有内容(IMHO)。
希望能对某人有所帮助。
我给你提供了可以parse_params
从命令行解析参数的函数。
--all
等于-all
平等all=all
)下面的脚本是一个复制粘贴的工作演示。请参阅show_use
函数以了解如何使用parse_params
。
局限性:
-d 1
)--any-param
和-anyparam
等价eval $(parse_params "$@")
必须在bash 函数内部使用(在全局范围内将不起作用)#!/bin/bash
# Universal Bash parameter parsing
# Parse equal sign separated params into named local variables
# Standalone named parameter value will equal its param name (--force creates variable $force=="force")
# Parses multi-valued named params into an array (--path=path1 --path=path2 creates ${path[*]} array)
# Puts un-named params as-is into ${ARGV[*]} array
# Additionally puts all named params as-is into ${ARGN[*]} array
# Additionally puts all standalone "option" params as-is into ${ARGO[*]} array
# @author Oleksii Chekulaiev
# @version v1.4.1 (Jul-27-2018)
parse_params ()
{
local existing_named
local ARGV=() # un-named params
local ARGN=() # named params
local ARGO=() # options (--params)
echo "local ARGV=(); local ARGN=(); local ARGO=();"
while [[ "$1" != "" ]]; do
# Escape asterisk to prevent bash asterisk expansion, and quotes to prevent string breakage
_escaped=${1/\*/\'\"*\"\'}
_escaped=${_escaped//\'/\\\'}
_escaped=${_escaped//\"/\\\"}
# If equals delimited named parameter
nonspace="[^[:space:]]"
if [[ "$1" =~ ^${nonspace}${nonspace}*=..* ]]; then
# Add to named parameters array
echo "ARGN+=('$_escaped');"
# key is part before first =
local _key=$(echo "$1" | cut -d = -f 1)
# Just add as non-named when key is empty or contains space
if [[ "$_key" == "" || "$_key" =~ " " ]]; then
echo "ARGV+=('$_escaped');"
shift
continue
fi
# val is everything after key and = (protect from param==value error)
local _val="${1/$_key=}"
# remove dashes from key name
_key=${_key//\-}
# skip when key is empty
# search for existing parameter name
if (echo "$existing_named" | grep "\b$_key\b" >/dev/null); then
# if name already exists then it's a multi-value named parameter
# re-declare it as an array if needed
if ! (declare -p _key 2> /dev/null | grep -q 'declare \-a'); then
echo "$_key=(\"\$$_key\");"
fi
# append new value
echo "$_key+=('$_val');"
else
# single-value named parameter
echo "local $_key='$_val';"
existing_named=" $_key"
fi
# If standalone named parameter
elif [[ "$1" =~ ^\-${nonspace}+ ]]; then
# remove dashes
local _key=${1//\-}
# Just add as non-named when key is empty or contains space
if [[ "$_key" == "" || "$_key" =~ " " ]]; then
echo "ARGV+=('$_escaped');"
shift
continue
fi
# Add to options array
echo "ARGO+=('$_escaped');"
echo "local $_key=\"$_key\";"
# non-named parameter
else
# Escape asterisk to prevent bash asterisk expansion
_escaped=${1/\*/\'\"*\"\'}
echo "ARGV+=('$_escaped');"
fi
shift
done
}
#--------------------------- DEMO OF THE USAGE -------------------------------
show_use ()
{
eval $(parse_params "$@")
# --
echo "${ARGV[0]}" # print first unnamed param
echo "${ARGV[1]}" # print second unnamed param
echo "${ARGN[0]}" # print first named param
echo "${ARG0[0]}" # print first option param (--force)
echo "$anyparam" # print --anyparam value
echo "$k" # print k=5 value
echo "${multivalue[0]}" # print first value of multi-value
echo "${multivalue[1]}" # print second value of multi-value
[[ "$force" == "force" ]] && echo "\$force is set so let the force be with you"
}
show_use "param 1" --anyparam="my value" param2 k=5 --force --multi-value=test1 --multi-value=test2
show_use "$@"
EasyOptions不需要任何解析:
## 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
如果#1您已安装它,而#2您打算在同一平台上运行它,则getopts效果很好。例如,OSX和Linux在这方面的行为有所不同。
这是一个支持equals,non-equals和boolean标志的(非getopts)解决方案。例如,您可以通过以下方式运行脚本:
./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
在扩展@ bruno-bronosky的答案时,我添加了一个“预处理器”来处理一些常见的格式设置:
--longopt=val
成--longopt val
-xyz
成-x -y -z
--
指示标志的结束#!/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[@]}"
有几种解析cmdline args的方法(例如,GNU getopt(不可移植)vs BSD(OSX)getopt vs getopts)-都是有问题的。这个解决方案是
=
分隔符-vxf
示例:任何
# 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=${@}"
我想提供我的选项解析版本,该版本允许以下操作:
-s p1
--stage p1
-w somefolder
--workfolder somefolder
-sw p1 somefolder
-e=hello
也允许这样做(可能是不需要的):
-s--workfolder p1 somefolder
-se=hello p1
-swe=hello p1 somefolder
您必须在使用前决定是否在选项上使用=。这是为了保持代码干净。
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
这是我的解决方案。它非常灵活,并且与其他应用程序不同,它不需要外部程序包并且可以干净地处理剩余的参数。
用法是: ./myscript -flag flagvariable -otherflag flagvar2
您所要做的就是编辑有效标志行。它加上一个连字符并搜索所有参数。然后将下一个参数定义为标志名称,例如
./myscript -flag flagvariable -otherflag flagvar2
echo $flag $otherflag
flagvariable flagvar2
主要代码(简短版本,详细示例如下,还有错误输出的版本):
#!/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
内置回显示例的详细版本:
#!/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
最后一个,如果传递了无效的参数,则会出错。
#!/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
优点:它的作用非常好。它保留了未使用的参数,而这里的许多其他解决方案则没有。它还允许在脚本中无需手动定义就调用变量。如果没有给出相应的参数,它也允许预填充变量。(请参见详细示例)。
缺点:无法解析单个复杂的arg字符串,例如-xcvf将作为单个参数进行处理。您可以轻松地向我的代码中编写其他代码,以添加此功能。
我想提交我的项目:https : //github.com/flyingangel/argparser
source argparser.sh
parse_args "$@"
就那么简单。将使用与参数同名的变量填充环境
请注意,这getopt(1)
是AT&T的短暂错误。
getopt创建于1984年,但由于它实际上不可用而已于1986年被埋葬。
一个getopt
非常过时的事实的证明是getopt(1)
手册页仍然提到"$*"
而不是"$@"
,它是在1986年与Bourne Shell一起添加的。getopts(1)
内置,以便处理内部带有空格的参数。
顺便说一句:如果您有兴趣解析shell脚本中的长选项,则可能想知道getopt(3)
libc(Solaris)的实现,并且ksh93
都添加了统一的长选项实现,该实现支持长选项作为短选项的别名。这将导致,ksh93
并通过Bourne Shell
来为长选项实现统一的接口getopts
。
Bourne Shell手册页中的长选项示例:
getopts "f:(file)(input-file)o:(output-file)" OPTX "$@"
显示了在Bourne Shell和ksh93中可以使用多长时间的选项别名。
请参阅最新的Bourne Shell的手册页:
http://schillix.sourceforge.net/man/man1/bosh.1.html
以及来自OpenSolaris的getopt(3)的手册页:
http://schillix.sourceforge.net/man/man3c/getopt.3c.html
最后,使用getopt(1)手册页来验证过时的$ *:
我已经写了一个bash助手来编写一个不错的bash工具
项目主页:https : //gitlab.mbedsys.org/mbedsys/bashopts
例:
#!/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
将提供帮助:
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:"")
请享用 :)
这是我的方法-使用regexp。
-qwerty
-q -w -e
--qwerty
=
提供属性,但是属性会匹配,直到遇到连字符+空格“定界符”为止,因此--q=qwe ty
qwe ty
是一种属性-o a -op attr ibute --option=att ribu te --op-tion attribute --option att-ribute
是有效的脚本:
#!/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
假设我们创建一个名为test_args.sh
如下的shell脚本
#!/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"
运行以下命令后:
sh test_args.sh -year 2017 -flag -month 12 -day 22
输出为:
year=2017 month=12 day=22 flag=true
使用bash-modules中的模块“参数”
例:
#!/bin/bash
. import.sh log arguments
NAME="world"
parse_arguments "-n|--name)NAME;S" -- "$@" || {
error "Cannot parse command line."
exit 1
}
info "Hello, $NAME!"
在位置参数之间自由混合标志:
./script.sh dumbo 127.0.0.1 --environment=production -q -d
./script.sh dumbo --environment=production 127.0.0.1 --quiet -d
可以用相当简洁的方法来完成:
# 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
这是usualy更清楚不能混合--flag=value
和--flag value
风格。
./script.sh dumbo 127.0.0.1 --environment production -q -d
这有点儿易读,但仍然有效
./script.sh dumbo --environment production 127.0.0.1 --quiet -d
资源
# 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
这是一个getopts,它使用最少的代码即可实现解析,并允许您使用带子字符串的eval定义在一种情况下要提取的内容。
基本上 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}"
}
在这里将变量声明为局部变量,而不是全局变量。
称为:
myrsync ---sourceurl http://abc.def.g ---sourceuser myuser ...
$ {k:3}基本上是一个子字符串,用于---
从密钥中删除第一个字符串。
知道这也可能很有用,您可以设置一个值,如果有人提供输入,则用该值覆盖默认值。
myscript.sh -f ./serverlist.txt或仅./myscript.sh(需要使用默认值)
#!/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}"
与Bruno Bronosky发布的解决方案类似,这是不使用的解决方案getopt(s)
。
我的解决方案的主要区别特征在于,它允许将选项串联在一起,就像tar -xzf foo.tar.gz
等于tar -x -z -f foo.tar.gz
。就像在等中一样tar
,ps
对于一系列短选项,前导连字符是可选的(但是可以轻松更改)。也支持长选项(但是,当一个块以一个开头时,则需要两个前导连字符)。
#!/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
有关示例用法,请参见下面的示例。
出于什么价值,带有参数的选项不是最后一个(只需要长选项)。因此,例如在tar
(至少在某些实现方式中)f
选项必须位于最后,因为文件名紧随其后(tar xzf bar.tar.gz
有效但tar xfz bar.tar.gz
无效),在此情况并非如此(请参阅后面的示例)。
作为另一个好处,选项参数由带有必需选项的参数按选项顺序使用。只需使用命令行abc X Y Z
(或-abc X Y Z
)在此处查看脚本的输出即可:
Option AAA activated!
Option BBB with argument 'X' activated!
Option CCC with arguments 'Y' and 'Z' activated!
同样,您也可以在选项块中使用长选项,因为它们出现在选项块的最后。因此,以下命令行都是等效的(包括选项及其参数的处理顺序):
-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
所有这些导致:
Option CCC with arguments 'Z' and 'Y' activated!
Option BBB with argument 'X' activated!
Option AAA activated!
Doing something...
带有一些可选参数的选项应该可以通过一些工作来实现,例如,通过检查是否存在不带连字符的块;然后,用户需要在带参数的块之后的每个块前面放置连字符。也许这太复杂以至于无法与用户进行交流,所以在这种情况下最好只要求使用连字符。
使用多个可能的参数,事情变得更加复杂。我建议不要通过确定某个参数是否适合它来使选项变得更聪明(例如,使用选项仅将数字作为可选参数),因为这将来可能会中断。
我个人更喜欢其他选项而不是可选参数。
就像带有可选参数一样,我也不喜欢这样做(顺便说一句,是否有一个讨论不同参数样式的利弊的线程?),但是如果您希望这样做,可以自己实现,就像在http://上完成一样。mywiki.wooledge.org/BashFAQ/035#Manual_loop带有--long-with-arg=?*
case语句,然后剥离等号(这是BTW,该站点说,可以通过一些努力进行参数串联,但是“留给读者[作为练习] ”,这让我信服了他们,但我还是从零开始。
兼容POSIX,甚至可以在我必须处理的古老Busybox设置上运行(例如和cut
,head
并且getopts
缺少)。
zparseopts -D -E -M -- d=debug -debug=d
同时使用-d
和--debug
,如果使用其中之一,则将返回0或1。参考:zsh.org/mla/users/2011/msg00350.html$debug
echo $+debug[1]