我在下面有一个脚本来源bash脚本的目录,然后解析命令的标志以从源文件运行特定的功能。
在scripts
目录中给出此功能:
function reggiEcho () {
echo $1
}
以下是当前输出的一些示例
$ reggi --echo hello
hello
$ reggi --echo hello world
hello
$ reggi --echo "hello world"
hello
$ reggi --echo "hello" --echo "world"
hello
world
正如你所看到的那样,引用的参数不应该被尊重,因为它们应该是“hello world”应该正确回应。
这是脚本,问题在while
循环内。
如何解析这些标志,并保持将引用的参数传入函数?
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
STR="$(find $DIR/scripts -type f -name '*.sh' -print)"
ARR=( $STR )
TUSAGE="\n"
for f in "${ARR[@]}"; do
if [ -f $f ]
then
. $f --source-only
if [ -z "$USAGE" ]
then
:
else
TUSAGE="$TUSAGE \t$USAGE\n"
fi
USAGE=""
else
echo "$f not found"
fi
done
TUSAGE="$TUSAGE \t--help (shows this help output)\n"
function usage() {
echo "Usage: --function <args> [--function <args>]"
echo $TUSAGE
exit 1
}
HELP=false
cmd=()
while [ $# -gt 0 ]; do # loop until no args left
if [[ $1 = '--help' ]] || [[ $1 = '-h' ]] || [[ $1 = '--h' ]] || [[ $1 = '-help' ]]; then
HELP=true
fi
if [[ $1 = --* ]] || [[ $1 = -* ]]; then # arg starts with --
if [[ ${#cmd[@]} -gt 0 ]]; then
"${cmd[@]}"
fi
top=`echo $1 | tr -d -` # remove all flags
top=`echo ${top:0:1} | tr '[a-z]' '[A-Z]'`${top:1} # make sure first letter is uppercase
top=reggi$top # prepend reggi
cmd=( "$top" ) # start new array
else
echo $1
cmd+=( "$1" )
fi
shift
done
if [[ "$HELP" = true ]]; then
usage
elif [[ ${#cmd[@]} -gt 0 ]]; then
${cmd[@]}
else
usage
fi
答案 0 :(得分:0)
这个脚本中有很多地方你有变量引用而没有双引号。这意味着变量&#39;值将受到单词吐痰和通配符扩展的影响,这可能会产生各种奇怪的效果。
您看到的具体问题是由于倒数第四行${cmd[@]}
上的不带引号的变量引用。使用cmd=( echo "hello world" )
时,分词会使其等同于echo hello world
而不是echo "hello world"
。
修复一行将修复当前问题,但是有许多其他未引用的变量引用可能会在以后引起其他问题。我建议修复所有。赛勒斯&#39; shellcheck.net的建议很好地指出了它们,并且还会注意到我在这里所涵盖的其他一些问题。它不会提到的一件事是你应该避免使用全大写变量名(DIR
,TUSAGE
等) - 有一堆具有特殊含义的全大写变量,以及它很容易意外地重复使用其中一个并结束了奇怪的效果。小写和混合大小写变量更安全。
我还建议不要在字符串中使用\t
和\n
,并依靠echo
将它们分别转换为制表符和换行符。某些版本的echo
会自动执行此操作,有些版本需要-e
选项告诉他们这样做,有些会打印&#34; -e&#34;作为他们产出的一部分......这是一团糟。在bash中,您可以使用$'...'
直接翻译这些转义序列,例如:
tusage="$tusage"$' \t--help (shows this help output)\n' # Note mixed quoting modes
echo "$tusage" # Note that double-quoting is *required* for this to work right
您还应该修复文件列表,这样它就不依赖于不加引号(请参阅chepner的评论)。如果您不需要扫描$ DIR /脚本的子目录,可以使用简单的通配符(注意小写变量并且var是双引号,但通配符不是)来执行此操作:
arr=( "$dir/scripts"/*.sh )
如果你需要查看子目录,那就更复杂了。如果你有bash v4,你可以使用globstar通配符,如下所示:
shopt -s globstar
arr=( "$dir/scripts"/**/*.sh )
如果您的脚本可能必须在bash v3下运行,请参阅BashFAQ #20: "How can I find and safely handle file names containing newlines, spaces or both?",或者只使用它:
while IFS= read -r -d '' f <&3; do
if [ -f $f ]
# ... etc
done 3< <(find "$dir/scripts" -type f -name '*.sh' -print0)
(那是我最喜欢的 - 只是用于迭代find
匹配的习惯用语。虽然它确实需要bash,但不是一些通用的POSIX shell。)