问:
说,我有一个用这一行调用的脚本:
./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile
或者这个:
./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile
什么是可接受的解析方式,以便在每种情况下(或两者的某种组合) v 、 v、 v、f 和 $d 都将设置为 true 并且 $outFile 将等于 {6 }?
答1:
huntsbot.com – 程序员副业首选,一站式外包任务、远程工作、创意产品分享订阅平台。
Bash 空格分隔(例如,–option 参数)
cat >/tmp/demo-space-separated.sh <<'EOF'
#!/bin/bash
POSITIONAL_ARGS=()
while [[ $# -gt 0 ]]; do
case $1 in
-e|--extension)
EXTENSION="$2"
shift # past argument
shift # past value
;;
-s|--searchpath)
SEARCHPATH="$2"
shift # past argument
shift # past value
;;
--default)
DEFAULT=YES
shift # past argument
;;
-*|--*)
echo "Unknown option $1"
exit 1
;;
*)
POSITIONAL_ARGS+=("$1") # save positional arg
shift # past argument
;;
esac
done
set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters
echo "FILE EXTENSION = ${EXTENSION}"
echo "SEARCH PATH = ${SEARCHPATH}"
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 /etc/hosts
复制粘贴上面的块的输出
FILE EXTENSION = conf
SEARCH PATH = /etc
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
用法
demo-space-separated.sh -e conf -s /etc /etc/hosts
Bash Equals-Separated(例如,–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
;;
--default)
DEFAULT=YES
shift # past argument with no value
;;
-*|--*)
echo "Unknown option $i"
exit 1
;;
*)
;;
esac
done
echo "FILE EXTENSION = ${EXTENSION}"
echo "SEARCH PATH = ${SEARCHPATH}"
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 /etc/hosts
复制粘贴上面的块的输出
FILE EXTENSION = conf
SEARCH PATH = /etc
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
用法
demo-equals-separated.sh -e=conf -s=/etc /etc/hosts
为了更好地理解 KaTeX parse error: Expected '}', got '#' at position 3: {i#̲*=},请在 this gui…i"或调用 两个 不必要的子流程的
echo “$i” | sed ‘s/[^=]*=//’`。
将 bash 与 getopt[s] 一起使用
getopt(1) 限制(较旧、相对较新的 getopt 版本):
无法处理空字符串参数
无法处理带有嵌入空格的参数
较新的 getopt 版本没有这些限制。有关详细信息,请参阅这些 docs。
POSIX getopts
此外,POSIX shell 和其他提供的 getopts 没有这些限制。我包含了一个简单的 getopts 示例。
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
用法
demo-getopts.sh -vf /etc/hosts foo bar
getopts 的优点是:
它更便携,并且可以在 dash 等其他 shell 中工作。它可以以典型的 Unix 方式自动处理多个单个选项,例如 -vf 文件名。
getopts 的缺点是它只能处理短选项(-h,而不是 --help)而无需额外的代码。
getopts tutorial 解释了所有语法和变量的含义。在 bash 中,还有 help getopts,它可能提供信息。
这是真的吗?根据 Wikipedia,有一个更新的 GNU 增强版本的 getopt,它包括 getopts 的所有功能,然后是一些功能。 Ubuntu 13.04 上的 man getopt 输出 getopt - parse command options (enhanced) 作为名称,所以我认为这个增强版本现在是标准的。
在你的系统上某种方式是一个非常薄弱的前提来建立“标准”的假设。
@Livven,getopt 不是 GNU 实用程序,它是 util-linux 的一部分。
如果您使用 -gt 0,请在 esac 之后删除您的 shift,将所有 shift 增加 1 并添加这种情况:*) break;; 您可以处理非可选参数。例如:pastebin.com/6DJ57HTc
getopts "h?vf:" 应该是不带问号的 getopts "hvf:"。无法识别的参数在 $opt 中存储为 ?。引自 man builtins:“The colon and question mark characters may not be used as option characters.”
答2:
huntsbot.com全球7大洲远程工作机会,探索不一样的工作方式
没有答案展示了增强的 getopt。并且 top-voted answer 具有误导性:它要么忽略 -vfd 样式短选项(由 OP 请求),要么忽略位置参数后的选项(也由 OP 请求);它忽略了解析错误。反而:
使用来自 util-linux 或以前的 GNU glibc.1 的增强型 getopt
它与 GNU glibc 的 C 函数 getopt_long() 一起使用。
此页面上没有其他解决方案可以做到这一切:在 arguments2 中处理空格、引用字符甚至二进制(非增强的 getopt 无法做到这一点)它可以在最后处理选项: script.sh -o outFile file1 file2 -v (getopts 不这样做)允许 =-style 长选项:script.sh --outfile=fileOut --infile fileIn(如果自解析则允许两者都很长)允许组合短选项,例如 -vfd(如果自解析则实际工作) 允许触摸选项参数,例如 -oOutfile 或 -vfdoOutfile
在 arguments2 中处理空格、引用字符甚至二进制(非增强的 getopt 不能这样做)
它可以在最后处理选项: script.sh -o outFile file1 file2 -v (getopts 不这样做)
允许 =-style 长选项: script.sh --outfile=fileOut --infile fileIn (如果自解析,则允许两者都很长)
允许组合短选项,例如 -vfd (如果自我解析,则实际工作)
允许触摸选项参数,例如 -oOutfile 或 -vfdoOutfile
已经太老了3,以至于没有任何 GNU 系统缺少它(例如,任何 Linux 都有它)。
您可以使用以下命令测试它的存在:getopt --test → return value 4。
其他 getopt 或 shell-builtin getopt 的用途有限。
以下调用
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
# More safety, by turning some bugs into errors.
# Without `errexit` you don’t need ! and can replace
# ${PIPESTATUS[0]} with a simple $?, but I prefer safety.
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
# option --output/-o requires 1 argument
LONGOPTS=debug,force,output:,verbose
OPTIONS=dfo:v
# -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 年)
谢谢你。刚刚从 en.wikipedia.org/wiki/Getopts 的功能表中确认,如果您需要对长选项的支持,并且您不在 Solaris 上,那么 getopt 是可行的方法。
我相信 getopt 的唯一警告是,它不能方便地在包装脚本中使用,其中一个可能只有几个特定于包装脚本的选项,然后将非包装脚本选项传递给包装的可执行文件,完好无损。假设我有一个名为 mygrep 的 grep 包装器,并且我有一个特定于 mygrep 的选项 --foo,那么我不能执行 mygrep --foo -A 2,并让 -A 2 自动传递给 grep;我需要做mygrep --foo -- -A 2。 这里是您解决方案之上的my implementation。
@bobpaul 您关于 util-linux 的陈述是错误的并且具有误导性:该软件包在 Ubuntu/Debian 上被标记为“必需”。因此,它始终被安装。 – 你在谈论哪些发行版(你说它需要故意安装)?
请注意,至少在当前 10.14.3 之前,这在 Mac 上不起作用。发布的 getopt 是 1999 年的 BSD getopt...
@jjj 脚注 1 涵盖 OS X。 – 对于 OS X 开箱即用的解决方案,请查看其他问题和答案。或者说实话:对于真正的编程,不要使用 bash。 ;-)
答3:
huntsbot.com高效搞钱,一站式跟进超10+任务平台外包需求
部署.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"
用法:
./deploy.sh -t dev -u
# OR:
./deploy.sh --target dev --uglify
这就是我正在做的事情。如果我想支持以布尔标志 ./script.sh --debug dev --uglify fast --verbose 结束行,则必须 while [[ "$#" > 1 ]]。示例:gist.github.com/hfossli/4368aa5a577742c3c9f9266ed214aa58
哇!简单干净!这就是我使用它的方式:gist.github.com/hfossli/4368aa5a577742c3c9f9266ed214aa58
这比粘贴到每个脚本中要好得多,而不是处理源代码或让人们想知道您的功能实际从哪里开始。
警告:这允许重复的参数,最新的参数为准。例如 ./script.sh -d dev -d prod 将导致 deploy == 'prod'。反正我用过:P :) :+1:
很好的答案,tnx!我把它缩短了一点 - while (( "$#" )); do 而不是 while [[ "$#" -gt 0 ]]; do
答4:
HuntsBot周刊–不定时分享成功产品案例,学习他们如何成功建立自己的副业–huntsbot.com
从 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}
为了更好地理解 KaTeX parse error: Expected '}', got '#' at position 3: {i#̲*=},请在 this gui…i"或调用 两个 不必要的子流程的
echo “$i” | sed ‘s/[^=]*=//’`。
整洁的!虽然这不适用于空格分隔的参数 à la mount -t tempfs ...。可以通过 while [ $# -ge 1 ]; do param=$1; shift; case $param in; -p) prefix=$1; shift;; 等解决此问题
这无法处理 -vfd 样式组合的短选项。
如果您想通用地评估 --option 和 -option 而不是每次都重复 OPTION=$i,请使用 -*=*) 作为匹配模式和 eval ${i##*-}。
答5:
huntsbot.com聚合了超过10+全球外包任务平台的外包需求,寻找外包任务与机会变的简单与高效。
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
这个解决方案:
处理 -n arg 和 --name=arg
最后允许参数
如果有任何拼写错误,则显示正常错误
兼容,不使用 bashisms
可读,不需要在循环中维护状态
抱歉耽搁了。在我的脚本中,handle_argument 函数接收所有非选项参数。您可以将该行替换为您想要的任何内容,可能是 *) die "unrecognized argument: $1" 或将 args 收集到变量 *) args+="$1"; shift 1;; 中。
惊人!我已经测试了几个答案,但这是唯一适用于所有情况的答案,包括许多位置参数(在标志之前和之后)
简洁的代码,但使用 -n 而没有其他 arg 由于 shift 2 上的错误导致无限循环,发出 shift 两次而不是 shift 2。建议编辑。
我进行了编辑(正在等待审核)以添加一些有用的功能,同时保持代码简洁明了。对于更高级的功能,例如单个参数中的多个单字母选项,您应该尝试 getopt 或 getopts。
答6:
huntsbot.com洞察每一个产品背后的需求与收益,从而捕获灵感
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”分解选项并将每个选项放在自己的参数中。它还添加了“–”。
使用 $* 是对 getopt 的错误使用。 (它使用空格来处理参数。)请参阅 my answer 以了解正确用法。
为什么要让它变得更复杂?
@Matt J,如果您使用“$i”而不是$i,脚本的第一部分(对于i)将能够处理其中包含空格的参数。 getopts 似乎无法处理带空格的参数。在 for i 循环中使用 getopt 有什么好处?
答7:
一个优秀的自由职业者,应该有对需求敏感和精准需求捕获的能力,而huntsbot.com提供了这个机会
我发现在脚本中编写可移植解析非常令人沮丧,以至于我编写了 Argbash - 一个 FOSS 代码生成器,可以为您的脚本生成参数解析代码,而且它具有一些不错的功能:
https://argbash.io
感谢您编写 argbash,我刚刚使用它,发现它运行良好。我主要选择 argbash,因为它是支持 OS X 10.11 El Capitan 上的旧 bash 3.x 的代码生成器。唯一的缺点是,与调用模块相比,代码生成器方法意味着您的主脚本中有相当多的代码。
您实际上可以使用 Argbash,它为您生成量身定制的解析库,您可以将其包含在您的脚本中,或者您可以将它放在单独的文件中并直接获取它。我添加了一个 example 来证明这一点,并且我也在文档中更明确地说明了这一点。
很高兴知道。该示例很有趣,但仍然不是很清楚-也许您可以将生成的脚本的名称更改为“parse_lib.sh”或类似名称,并显示主脚本调用它的位置(例如在包装脚本部分中,这是更复杂的用例)。
这些问题已在 argbash 的最新版本中得到解决:文档已得到改进,引入了快速入门 argbash-init 脚本,您甚至可以在 argbash.io/generate 上在线使用 argbash
答8:
huntsbot.com洞察每一个产品背后的需求与收益,从而捕获灵感
我以较早的答案为起点来整理我旧的临时参数解析。然后我重构了以下模板代码。它处理长参数和短参数,使用 = 或空格分隔的参数,以及组合在一起的多个短参数。最后,它将所有非参数参数重新插入到 $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。并且使用 = 将短选项与其论点分开是不寻常的......
我在这段有用的代码中遇到了两个问题:1)“-c=foo”情况下的“shift”最终吃掉了下一个参数;和 2) 'c' 不应包含在可组合空头期权的“[cfr]”模式中。
答9:
打造属于自己的副业,开启自由职业之旅,从huntsbot.com开始!
# 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 value
适用于您可以在 bash 中使用的任何 arg 名称含义 -a 或 -arg 或 --arg 或 -arg 或其他
含义 -a 或 -arg 或 --arg 或 -arg 或其他
纯粹的狂欢。无需学习/使用 getopt 或 getopts
缺点:
不能组合 args 意味着没有 -abc。你必须做 -a -b -c
意思是没有-abc。你必须做 -a -b -c
我在这里有个问题。为什么使用 shift; OUTPUTFILE="$1" 而不是 OUTPUTFILE="$2"?也许它有一个简单的答案,但我是 bash 的新手
我相信你可以做任何一个,这真的只是个人喜好。在这种情况下,我只想在任何地方都将 $1 作为“活动”参数
答10:
HuntsBot周刊–不定时分享成功产品案例,学习他们如何成功建立自己的副业–huntsbot.com
此示例说明如何使用 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 <&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
简短、中肯、易读,并且几乎可以处理所有事情(恕我直言)。
希望对某人有所帮助。
这是最好的答案之一。
答11:
huntsbot.com洞察每一个产品背后的需求与收益,从而捕获灵感
扩展@bruno-bronosky 的答案,我添加了一个“预处理器”来处理一些常见的格式:
将 --longopt=val 扩展为 --longopt val
将 -xyz 扩展为 -x -y -z
支持 – 表示标志的结束
显示意外选项的错误
紧凑且易于阅读的选项开关
#!/bin/bash
# Report usage
usage() {
echo "Usage:"
echo "$(basename "$0") [options] [--] [file1, ...]"
}
invalid() {
echo "ERROR: Unrecognized argument: $1" >&2
usage
exit 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; exit 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[@]}"
这看起来很棒 - 但想知道 END_OF_OPT=1 是否真的需要在此行:--*) ARGV+=("$arg"); END_OF_OPT=1 ;;。如果留在其中,如果它包含在 --quiet 之后(或任何其他长样式布尔选项),它将无法解析 --username=fred。例如,script.sh --quiet --username=fred 以 Unrecognized argument: --username=fred 失败(尽管 script.sh --quiet --username fred 工作正常)。我在我的脚本中取出了那个 END_OF_OPT=1,现在它可以工作了,但不确定这是否会破坏我不知道的其他情况。
原文链接:https://www.huntsbot.com/qa/Zv8Y/how-do-i-parse-command-line-arguments-in-bash?lang=zh_CN&from=csdn
huntsbot.com汇聚了国内外优秀的初创产品创意,可按收入、分类等筛选,希望这些产品与实践经验能给您带来灵感。