繁体   English   中英

循环shell脚本参数并将带引号的参数传递给函数

[英]Looping over shell script arguments and passing quoted arguments to function

我下面有一个脚本,该脚本提供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

在此脚本中的很多地方,变量引用周围都没有双引号。 这意味着变量的值将受到单词分散和通配符扩展的影响,这可能会产生各种奇怪的影响。

您看到的特定问题是由于倒数第四行${cmd[@]}上没有引用的变量引用引起的。 使用cmd=( echo "hello world" ) ,单词拆分使它等效于echo hello world而不是echo "hello world"

解决这一问题将解决您当前的问题,但是还有许多其他未引用的变量引用可能在以后引起其他问题。 我建议修复所有这些问题。 赛勒斯(Cyrus)对shellcheck.net的推荐擅长于指出它们,并且还将指出我在这里不会涉及的其他一些问题。 它不会提及的一件事是,您应该避免使用全大写字母的变量名( DIRTUSAGE等)-一堆具有特殊含义的全大写字母变量,很容易意外地重用其中的一个并绕开产生怪异的效果。 小写和大小写混合的变量更安全。

我还建议不要在字符串中使用\\t\\n ,而应该依靠echo将它们分别转换为制表符和换行符。 某些版本的echo会自动执行此操作,某些版本需要-e选项来告诉他们执行此操作,某些版本会在其输出中打印“ -e” ...这是一团糟。 在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 / scripts的子目录,则可以使用一个简单的通配符(请注意小写的vars,并且该var被双引号括起来,但是通配符不是):

arr=( "$dir/scripts"/*.sh )

如果您需要查看子目录,则更为复杂。 如果您具有bash v4,则可以使用globstar通配符,如下所示:

shopt -s globstar
arr=( "$dir/scripts"/**/*.sh )

如果您的脚本可能必须在bash v3下运行,请参见BashFAQ#20:“如何查找并安全地处理包含换行符,空格或两者的文件名?” ,或仅使用此代码:

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。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM