解析 shell 脚本参数

12
$myscript.sh -host blah -user blah -pass blah

我想将参数传递给它。

我习惯于使用$1$2$3....但我想开始命名它们。


2
考虑不使用参数选项,而是通过环境变量传递值:例如“host=hostname user=me myscript.sh”。 - William Pursell
复制自https://dev59.com/AXVC5IYBdhLWcg3ww0Hr,其中有一个非常好的答案,比较了纯Bash“switch”、“getopts”(POSIX shell内置)和“getopt”(不建议使用,除非它是util-linux版本并且您使用其非POSIX功能以避免空参数等问题)。 - Peter Cordes
5个回答

16

在sh中,有很多解析参数的方法。 Getopt是不错的选择。这是一个手动解析参数的简单脚本:

#!/bin/sh
# WARNING: see discussion and caveats below
# this is extremely fragile and insecure

while echo $1 | grep -q ^-; do
    # Evaluating a user entered string!
    # Red flags!!!  Don't do this
    eval $( echo $1 | sed 's/^-//' )=$2
    shift
    shift
done

echo host = $host
echo user = $user
echo pass = $pass
echo args = $@

一个示例运行如下:

$ ./a.sh -host foo -user me -pass secret some args
host = foo
user = me
pass = secret
args = some args

请注意,这种方法远非健壮,因为脚本会评估用户构造的字符串,从而导致存在大量安全漏洞。它只是作为一种可能的方式的例子。一个更简单的方法是要求用户将数据传递给环境变量。在Bourne shell中(即任何不属于csh家族的shell):
$ host=blah user=blah pass=blah myscript.sh

这个功能很好用,变量$host$user$pass将会在脚本中可用。

#!/bin/sh
echo host = ${host:?host empty or unset}
echo user = ${user?user not set}
...

3
使用 declare 而不是 eval 更加安全。 - Dennis Williamson
除了@DennisWilliamson的建议外,我建议删除| tr -d '\012',因为它不是必需的 - 命令替换会自动修剪所有尾随换行符。 - mklement0
@mklement0 我相信我实际上是在有意识地考虑内部换行符,这很奇怪,因为试图处理它是试图为一个极其脆弱的想法增加鲁棒性。这种方式如此脆弱,以至于太好笑了。 - William Pursell
这是一个使用declare而不使用sed(或eval)的示例:declare "${1#-}"=$2。请注意,这种方法无法处理两个或多个前导连字符或嵌入式连字符。此外,在Bash变量名称中不允许使用连字符。可以添加单独的步骤将连字符转换为下划线,因为下划线是允许的:var=${1#-}; declare "${var//-/_}"="$2" - Dennis Williamson

12

这里有一种简单的方法来处理长选项和短选项:

while [[ $1 == -* ]]; do
    case "$1" in
      -h|--help|-\?) show_help; exit 0;;
      -v|--verbose) verbose=1; shift;;
      -f) if [[ $# > 1 && $2 != -* ]]; then
            output_file=$2; shift 2
          else 
            echo "-f requires an argument" 1>&2
            exit 1
          fi ;;
      --) shift; break;;
      -*) echo "invalid option: $1" 1>&2; show_help; exit 1;;
    esac
done

如何轻松处理脚本命令行参数 (选项)?

获取。


1
+1;需要注意的是,该方法不能处理压缩选项,例如使用“-vf”代替“-v -f”。 - mklement0
d = (-_o)。感谢!已在/bin/sh上测试并运行(无需Bash)。 - Top-Master

10

1
请注意,getopts 是 Bash 内置命令,而 getopt 是外部实用程序。前者仅适用于短选项(例如 -e),而后者也适用于长选项(例如 -foo)。请注意,后者存在一些问题需要解决(较新版本的 getopt 问题较少)。 - Dennis Williamson

1
我采用了William Pursell的示例(并遵循Dennis Williamson的建议)来使用以下格式的参数: script -param1=value1 -param2=value2 ...
这里是带有单行参数解析器的代码(将其保存在文件“script”中):
#!/bin/bash

while echo $1 | grep ^- > /dev/null; do declare $( echo $1 | sed 's/-//g' | sed 's/=.*//g' | tr -d '\012')=$( echo $1 | sed 's/.*=//g' | tr -d '\012'); shift; done

echo host = $host
echo user = $user
echo pass = $pass

您可以这样调用:

script -host=aaa -user=bbb -pass=ccc

结果为:

host = aaa
user = bbb
pass = ccc

有没有更短的代码来解析参数?

0

这是我的方法:

host=""
user=""
pass=""
while [[ "$#" -gt 0 ]]; do
    case $1 in
        -h|--host) host="$2"; shift ;;
        -u|--user) user="$2"; shift ;;
        -p|--pass) pass="$2"; shift ;;
        *) echo "Unknown parameter passed: $1" ;;
    esac
    shift
done


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接