如何在 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
?
-
Bash 空格分隔(例如,
--option argument
)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 等于分隔(例如,
--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
为了更好地理解在本指南
${i#*=}
中搜索“子字符串删除” 。它在功能上等价于调用一个不需要的子进程或调用两个不需要的子进程。sed 's/[^=]*=//' <<< "$i"````echo "$i" | sed 's/[^=]*=//'
将 bash 与 getopt[s] 一起使用
getopt(1) 限制(较旧、相对较新的
getopt
版本):- 无法处理空字符串参数
- 无法处理带有嵌入空格的参数
较新的
getopt
版本没有这些限制。有关更多信息,请参阅这些文档。
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
是:- 它更便携,并且可以在其他 shell 中工作,例如
dash
. -vf filename
它可以像典型的 Unix 方式那样自动处理多个单个选项。
的缺点
getopts
是它只能处理短选项(-h
, not--help
)而无需额外代码。