如何解析Bash中的命令行参数?
说,我有一个脚本,被调用这一行:
./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
?
首选方法:使用不带getopt的直接bash [s]
我原本是在OP问的时候回答这个问题的。 这个Q / A得到了很多关注,所以我也应该提供非魔术方式来做到这一点。 我打算扩展guneysus的答案来解决讨厌的sed并且包括Tobias Kienzler的建议。
传递键值对参数的两种最常用的方法是:
直击Bash空间分离
用法./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
直击是等分的
用法./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#*=}
搜索“子串删除”。 它在功能上等同于`sed 's/[^=]*=//' <<< "$i"`
,它调用一个不必要的子进程或`echo "$i" | sed 's/[^=]*=//'`
`echo "$i" | sed 's/[^=]*=//'`
它调用两个不必要的子进程。
使用getopt [s]
来自:http://mywiki.wooledge.org/BashFAQ/035#getopts
切勿使用getopt(1)。 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
的优点是:
-vf filename
。 getopts
的缺点是它只能处理简短的选项( -h
,而不是--help
)而不会产生欺骗。
有一个getopts教程,它解释了所有的语法和变量的含义。 在bash中,也有help getopts
,这可能是信息。
没有回答提及增强的getopt。 最 -vfd
答案是误导性的:它忽略-vfd
风格的短期选项(由OP请求),位置参数之后的选项(OP也请求),它忽略解析错误。 代替:
getopt
getopt_long()
GNU glibc的C函数。 script.sh -o outFile file1 file2 -v
=
-style长选项: script.sh --outfile=fileOut --infile fileIn
getopt --test
→返回值4。 getopt
或shell- 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
getopt --test > /dev/null
if [[ $? -ne 4 ]]; then
echo "I’m sorry, `getopt --test` failed in this environment."
exit 1
fi
OPTIONS=dfo:v
LONGOPTIONS=debug,force,output:,verbose
# -temporarily store output to be able to check for errors
# -e.g. use “--options” parameter by name to activate quoting/enhanced mode
# -pass arguments only via -- "$@" to separate them correctly
PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTIONS --name "$0" -- "$@")
if [[ $? -ne 0 ]]; then
# e.g. $? == 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"
# 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系统”都提供了1个增强的getopt,包括Cygwin; 在OS X上尝试brew install gnu-getopt
2 POSIX exec()
约定没有可靠的方法在命令行参数中传递二进制NULL; 那些字节过早地结束了参数
3 1997年或之前发布的第一个版本(我只追溯到1997年)
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”将选项分开,并将每个选项放在它自己的参数中。 它还添加了“ - ”。
链接地址: http://www.djcxy.com/p/4123.html