说,我有一个用这一行调用的脚本:
./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile
或者这个:
./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile
解析这个的可接受方式是,在每种情况下(或两者的某种组合)$v
,$f
和$d
都将设置为{{1} }和true
将等于$outFile
?
答案 0 :(得分:2266)
传递键 - 值对参数的两种常用方法是:
--option argument
)(没有getopt [s])用法 ./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
--option=argument
)(没有getopt [s])用法./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
更好地了解${i#*=}
在this guide中搜索“子字符串删除”。它在功能上等同于`sed 's/[^=]*=//' <<< "$i"`
调用不必要的子进程或`echo "$i" | sed 's/[^=]*=//'`
调用两个不必要的子进程。
来自:http://mywiki.wooledge.org/BashFAQ/035#getopts
getopt(1)限制(旧的,相对较新的getopt
版本):
最近的getopt
版本没有这些限制。
此外,POSIX shell(和其他)提供getopts
,它没有这些限制。这是一个简单的getopts
示例:
#!/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
getopts
的优点是:
dash
之类的其他shell中使用。 -vf filename
。 getopts
的缺点是它只能在没有附加代码的情况下处理短选项(-h
,而不是--help
)。
有getopts tutorial解释了所有语法和变量的含义。在bash中,还有help getopts
,这可能是提供信息的。
答案 1 :(得分:448)
没有回答提到增强的getopt 。并且top-voted answer具有误导性:它会忽略-vfd
样式的短选项(由OP请求)或位置参数后的选项(也由OP请求);它忽略了解析错误。代替:
getopt
。 1 getopt_long()
GNU glibc的C函数一起使用。getopt
不能这样做)script.sh -o outFile file1 file2 -v
(getopts
不执行此操作)=
- 样式长选项: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"
1 增强型getopt可用于大多数“bash系统”,包括Cygwin;在OS X上尝试brew install gnu-getopt或sudo port install getopt
2 POSIX exec()
约定没有可靠的方法在命令行参数中传递二进制NULL;那些字节过早地结束了论点
3 1997年或之前发布的第一个版本(我只追溯到1997年)
答案 2 :(得分:117)
来自: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#*=}
在this guide中搜索“子字符串删除”。它在功能上等同于`sed 's/[^=]*=//' <<< "$i"`
调用不必要的子进程或`echo "$i" | sed 's/[^=]*=//'`
调用两个不必要的子进程。
答案 3 :(得分:103)
getopt()
/ getopts()
是个不错的选择。从here被盗:
这个小脚本中显示了“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”打破了选择和 把每个人都放在自己的论点中。它也是 添加了“ - ”。
答案 4 :(得分:90)
<强> 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
答案 5 :(得分:90)
冒着添加另一个要忽略的例子的风险,这是我的方案。
-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
答案 6 :(得分:39)
这个问题我迟到了4年,但是想要回馈。我使用前面的答案作为整理我的旧adhoc param解析的起点。然后我重构了以下模板代码。它使用=或空格分隔的参数处理长和短参数,以及组合在一起的多个短参数。最后,它将任何非参数参数重新插入到$ 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
答案 7 :(得分:26)
我的回答很大程度上基于the answer by 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
CONS:
无法合并args
这些是我能想到的唯一优点/缺点
答案 8 :(得分:26)
我发现在脚本中编写可移植解析的问题非常令人沮丧,因为我写了Argbash - 一个FOSS代码生成器,可以为脚本生成参数解析代码,并且它有一些很好的功能:
答案 9 :(得分:13)
我认为这个很简单,可以使用:
#!/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
答案 10 :(得分:13)
扩展@guneysus的优秀答案,这是一个调整,让用户可以使用他们喜欢的任何语法,例如
command -x=myfilename.ext --another_switch
VS
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
答案 11 :(得分:8)
如果安装了#1,那么getopts的效果很好,并且#2打算在同一平台上运行它。 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
答案 12 :(得分:8)
我给你的函数parse_params
将从命令行解析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
答案 13 :(得分:7)
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
答案 14 :(得分:6)
这是我在一个函数中的做法,以避免在堆栈中更高的位置同时运行getopts:
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
...
}
答案 15 :(得分:4)
请注意getopt(1)
是来自AT&amp; T的短暂生活错误。
getopt创建于1984年,但已于1986年埋葬,因为它并不真正可用。
getopt
非常过时这一事实的证明是getopt(1)
手册页仍然提到"$*"
而不是"$@"
,这已添加到Bourne Shell中1986与内置getopts(1)
shell一起处理内部空格的参数。
getopt(3)
的{{1}}实现都添加了统一的长选项实现支持长选项作为短选项的别名。这会导致ksh93
和ksh93
通过Bourne Shell
为长期选项实现统一的界面。
Bourne Shell手册页中的长选项示例:
getopts
显示在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)手册页验证过时的$ *:
答案 16 :(得分:4)
我想提供我的选项解析版本,允许以下内容:
-s p1
--stage p1
-w somefolder
--workfolder somefolder
-sw p1 somefolder
-e=hello
也允许这样做(可能不需要):
-s--workfolder p1 somefolder
-se=hello p1
-swe=hello p1 somefolder
您必须在使用前决定是否要在选项上使用=。这是为了保持代码清洁(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
答案 17 :(得分:4)
https://github.com/ko1nksm/getoptions
getoptions 是一个新的选项解析器(生成器),它使用POSIX兼容的Shell脚本编写,并于2020年8月发布。适用于那些希望在shell脚本中支持标准选项语法而又不残酷的人。支持的语法为-a
,+a
,-abc
,-vvv
,-p VALUE
,-pVALUE
,--flag
,{{1} },--no-flag
,--param VALUE
,--param=VALUE
,--option[=VALUE]
--no-option
。
它支持子命令,验证,缩写选项和自动帮助生成。并适用于大多数操作系统(Linux,macOS,BSD,Windows等)和所有POSIX Shell(破折号,bash,ksh,zsh等)。
--
它也是一个选项解析器生成器,生成以下选项解析代码。
#!/bin/sh
. ./lib/getoptions.sh
. ./lib/getoptions_help.sh
parser_definition() {
setup REST help:usage -- "Usage: ${2##*/} [options] [arguments]" ''
flag FLAG -f --flag -- "--flag option"
param PARAM -p --param -- "--param option"
option OPTION -o --option on:"default" -- "--option option"
disp :usage -h --help
disp VERSION --version
}
eval "$(getoptions parser_definition parse "$0")"
parse "$@"
eval "set -- $REST"
echo "FLAG: $FLAG"
echo "PARAM: $PARAM"
echo "OPTION: $OPTION"
printf ': %s\n' "$@" # Rest arguments
答案 18 :(得分:3)
我想分享我所做的解析选项。 这里的答案无法满足我的某些需求(例如子选项管理,var args,可选args等),因此我不得不提出以下建议:https://github.com/MihirLuthra/bash_option_parser
假设我们有一个名为fruit
的命令,其用法如下:
fruit ...
[-e|—-eat|—-chew]
[-c|--cut <how> <why>]
<command> [<args>]
-e
不带参数
-c
有两个参数,即如何切割和为什么切割
<command>
适用于诸如apple
,orange
等子选项(类似于git
具有子选项commit
,push
等)
所以要解析它:
source ./option_parser
parse_options \
',' 'OPTIONS' 'ARG_CNT' 'ARGS' 'self' '0' ';' '--' \
\
'-e' , '—-eat' , '—-chew' '0' \
'-c' , '—-cut' , '1 1' \
'apple' 'S' \
'orange' 'S' \
\
';' "$@"
现在,如果出现任何使用错误,可以使用option_parser_error_msg
进行打印,如下所示:
retval=$?
if [ $retval -ne 0 ]; then
option_parser_error_msg "$retval" 'OPTIONS'
exit 1
fi
要立即检查是否通过了某些选项,
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
对于子选项,例如apple
,其用法如下:
fruit apple ...
[—-eat-apple|—-chew-apple]
[-p|—-peel <how>]
我们可以检查它是否通过并按以下方式进行解析:
if [ -n "${OPTION[apple]}" ]
then
shift_count=${OPTION[apple]}
parse_options \
',' 'OPTIONS_apple' 'ARG_CNT_apple' 'ARGS_apple' \
'self' "$shift_count" ';' '--' \
\
'—-eat-apple' , '—-chew-apple' '0' \
'-p' , '—-peel' '1' \
\
';' "$@"
fi
子选项解析是通过将$shift_count
传递到parse_options
来完成的,这使得它在将args转移到子选项的args之后开始解析。
自述文件和示例中提供了详细说明 在repository中。
答案 19 :(得分:3)
我想提交我的项目:https://github.com/flyingangel/argparser
source argparser.sh
parse_args "$@"
就这么简单。环境中将填充与参数名称相同的变量
答案 20 :(得分:3)
假设我们创建一个名为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
答案 21 :(得分:2)
在位置参数之间自由混合标记:
./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
通常情况下,不要混用--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
答案 22 :(得分:2)
这是我的解决方案。它非常灵活,与其他人不同,不应该需要外部包,并且干净地处理剩余的参数。
用法是:./myscript -flag flagvariable -otherflag flagvar2
您所要做的就是编辑validflags行。它会使用连字符并搜索所有参数。然后它将下一个参数定义为标志名称,例如
./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
内置echo演示的详细版本:
#!/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
最后一个,如果传递了无效的-argument,则会出错。
#!/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将作为单个参数处理。您可以在某种程度上轻松地将其他代码写入我的添加此功能。
答案 23 :(得分:2)
我写下了一个可以轻松解析命令行参数的脚本-https://github.com/unfor19/bargs
UITableView.appearance().." in
$ bash example.sh -n Willy --gender male -a 99
Name: Willy
Age: 99
Gender: male
Location: chocolate-factory
$ bash example.sh -n Meir --gender male
[ERROR] Required argument: age
Usage: bash example.sh -n Willy --gender male -a 99
--person_name | -n [Willy] What is your name?
--age | -a [Required]
--gender | -g [Required]
--location | -l [chocolate-factory] insert your location
答案 24 :(得分:2)
我写了一个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:"")
享受:)
答案 25 :(得分:2)
这是我的方法 - 使用正则表达式。
-qwerty
-q -w -e
--qwerty
=
来提供属性,但属性匹配直到遇到连字符+空格&#34;分隔符&#34;,因此在--q=qwe ty
qwe ty
中是一个属性< / LI>
-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
答案 26 :(得分:1)
这里是一个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}基本上是一个子字符串,用于从密钥中删除第一个---
。
答案 27 :(得分:1)
我最终实现了 accepted answer 的 dash
(或 /bin/sh
)版本,基本上没有使用数组:
while [[ $# -gt 0 ]]; do
case "$1" in
-v|--verbose) verbose=1; shift;;
-o|--output) if [[ $# -gt 1 && "$2" != -* ]]; then
file=$2; shift 2
else
echo "-o requires file-path" 1>&2; exit 1
fi ;;
--)
while [[ $# -gt 0 ]]; do BACKUP="$BACKUP;$1"; shift; done
break;;
*)
BACKUP="$BACKUP;$1"
shift
;;
esac
done
# Restore unused arguments.
while [ -n "$BACKUP" ] ; do
[ ! -z "${BACKUP%%;*}" ] && set -- "$@" "${BACKUP%%;*}"
[ "$BACKUP" = "${BACKUP/;/}" ] && break
BACKUP="${BACKUP#*;}"
done
答案 28 :(得分:1)
我受到相对简单的answer by @bronson的启发,并试图尝试对其进行改进(而不会增加太多复杂性)。结果如下:
getopt*
-n [arg]
,-abn [arg]
,--name [arg]
和 --name=arg
样式的选项; $@
; --
强制将其余参数视为位置; getopt(s)
或外部工具(一个功能使用简单的sed
命令); # 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/
注意:我知道...具有二进制模式 0x01030307
的参数可能会破坏逻辑。但是,如果有人在命令行中传递这样的参数,那么他们应该得到它。
答案 29 :(得分:1)
我用它从最后迭代key => value。循环后捕获第一个可选参数。
用法是 ./script.sh optional-first-arg -key value -key2 value2
#!/bin/sh
a=$(($#-1))
b=$(($#))
while [ $a -gt 0 ]; do
eval 'key="$'$a'"; value="$'$b'"'
echo "$key => $value"
b=$(($b-2))
a=$(($a-2))
done
unset a b key value
[ $(($#%2)) -ne 0 ] && echo "first_arg = $1"
当然你可以从左到右做一些改变。
这段代码显示了 key => value 对和第一个参数(如果存在)。
#!/bin/sh
a=$((1+$#%2))
b=$((1+$a))
[ $(($#%2)) -ne 0 ] && echo "first_arg = $1"
while [ $a -lt $# ]; do
eval 'key="$'$a'"; value="$'$b'"'
echo "$key => $value"
b=$(($b+2))
a=$(($a+2))
done
unset a b key value
用 100,000 个参数进行测试,速度很快。
您还可以从左到右迭代 key => value 和 first optional arg 而不使用 eval :
#!/bin/sh
a=$(($#%2))
b=0
[ $a -eq 1 ] && echo "first_arg = $1"
for value; do
if [ $b -gt $a -a $(($b%2)) -ne $a ]; then
echo "$key => $value"
fi
key="$value"
b=$((1+$b))
done
unset a b key value
答案 30 :(得分:1)
Here is my improved solution of Bruno Bronosky's answer using variable arrays.
it lets you mix parameters position and give you a parameter array preserving the order without the options
#!/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[@]}
Will output for example:
$ ./test.sh parameter -s somefile --skip=.c --skip=.obj
parameter -s somefile --skip=.c --skip=.obj
SKIP = .c .obj
SOFT = 1
Parameters:
parameter somefile
答案 31 :(得分:1)
为扩展@ 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[@]}"
答案 32 :(得分:1)
示例:
#!/bin/bash
. import.sh log arguments
NAME="world"
parse_arguments "-n|--name)NAME;S" -- "$@" || {
error "Cannot parse command line."
exit 1
}
info "Hello, $NAME!"
答案 33 :(得分:1)
此示例显示如何使用getopt
和eval
以及HEREDOC
和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
简而言之,可读,并处理几乎所有事情(恕我直言)。
希望能有所帮助。
答案 34 :(得分:1)
当我尝试这个问题时,这个问题的最佳答案似乎有点儿麻烦 - 这是我的解决方案,我发现它更强大:
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
答案 35 :(得分:1)
这也可能有用,要知道,您可以设置一个值,如果有人提供输入,请使用该值覆盖默认值。
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}"
答案 36 :(得分:1)
与the solution Bruno Bronosky posted类似,此处不使用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...
带有可选参数的选项应该可以通过一些工作来实现,例如:通过向前看是否有一个没有连字符的块;然后,用户需要在具有可选参数的参数的块之后的每个块的前面放置连字符。也许这太复杂了,无法与用户沟通,所以在这种情况下只需要一个前导连字符。
随着多个可能的参数,事情变得更加复杂。我建议不要通过确定参数是否适合它来使选项变得聪明(例如,使用一个选项只需要一个数字作为可选参数),因为这可能在将来中断。
我个人赞成使用其他选项而不是可选参数。
就像可选参数一样,我不喜欢这个(BTW,是否有讨论不同参数样式的优点/缺点的线程?)但如果你想要这个,你可以自己实现它就像在{ {3}}使用--long-with-arg=?*
case语句,然后剥离等号(这是BTW网站,说可以通过一些努力进行参数连接,但是&#34;将[it]留作为练习读者&#34;这让我接受他们的话,但我从零开始。)
POSIX兼容,即使在我必须处理的古老Busybox设置上也是如此(例如cut
,head
和getopts
丢失。
答案 37 :(得分:0)
有几种解析cmdline args的方法(例如,GNU getopt(非可移植)与BSD(OSX)getopt与getopts)-都是有问题的。该解决方案是
=
分隔符示例:任何
# flag
-f
--foo
# option with arg
-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 - bit like a man page
SYNOPSIS
program-name.sh [-h|--help]
program-name.sh [-f|--foo]
[-b|--bar <arg>]
[--baz[=<arg>]]
[--]
FILE
REQUIRED ARGUMENTS
FILE
input file
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>
--
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. we can't use process substitution as OPTIND
# does not propagate across sub shells so we reassign output in OPTARG
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))
# remaining non-option handling
if [ "$#" -ne 1 ]; then
fatal "Did not specify required argument FILE" "See '${0} --help' for usage"
fi
file=$1
echo foo=$foo bar=$bar baz=$baz file=$file
答案 38 :(得分:0)
简单易修改,参数可以任意顺序。可以将其修改为采用任何形式的参数(-a,-a,a等)。
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