比如,我有一个脚本,用这行代码调用:
./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile
或者这个:
./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile
什么是公认的解析方式,以便在每种情况下(或两者的某种组合)$v、$f和$d都将设置为true,而$outFile将等于/fiz/someOtherFile?
比如,我有一个脚本,用这行代码调用:
./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile
或者这个:
./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile
什么是公认的解析方式,以便在每种情况下(或两者的某种组合)$v、$f和$d都将设置为true,而$outFile将等于/fiz/someOtherFile?
当前回答
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允许在末尾使用参数如果有任何拼写错误,则显示正常错误兼容,不使用抨击可读,不需要在循环中维护状态
其他回答
保留未处理参数的解决方案。包括演示。
这是我的解决方案。它非常灵活,不像其他软件包,不需要外部包,并且可以干净地处理遗留的参数。
用法为:/myscript-flag flag变量-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
带有内置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
最后一个,如果传递了一个无效的参数,这一个会出错。
#!/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将作为单个参数处理。不过,您可以很容易地在我的代码中编写额外的代码,以添加此功能。
此示例显示了如何使用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
简短,切中要害,可读,处理几乎所有的事情(IMHO)。
希望这对某人有所帮助。
另一个Shell参数分析器(ASAP)
符合POSIX,无getopt
我受到@bronson相对简单的回答的启发,并试图改进它(不增加太多复杂性)。结果如下:
使用-n[arg]、-abn[arg],--name[arg]和--name=arg样式中的任意一种选项;参数可以按任何顺序出现,循环后$@中只留下位置参数;使用--强制将剩余的参数视为位置参数;检测无效选项和缺少的参数;不依赖于getopt或外部工具(一个功能使用简单的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.
if [ "$#" != 0 ]; then
EOL=$(printf '\1\3\3\7')
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;;
# Arguments processing. You may remove any unneeded line after the 1st.
-|''|[!-]*) set -- "$@" "$opt";; # positional argument, rotate to the end
--*=*) 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的参数可能会破坏逻辑。但是,如果有人在命令行中传递这样的参数,他们应该得到它。
这也可能很有用:您可以设置一个值,如果有人提供输入,则使用该值覆盖默认值。
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}"
我使用它从末尾迭代key=>值。循环后捕获第一个可选参数。
用法为/script.sh可选的第一个参数-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=>值对和第一个参数(如果存在)。
#!/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
测试了100000个参数,速度很快。
您还可以从左到右迭代key=>value和第一个可选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