【问题标题】:getopt erroneously caches argumentsgetopt 错误地缓存参数
【发布时间】:2018-06-15 16:40:18
【问题描述】:

我在我的 bash_aliases 中创建了一个脚本,以便更轻松地通过 SSH 连接到服务器。但是,我遇到了一些我不理解的奇怪行为。以下脚本按您的预期工作,除非它被重复使用。

如果我第一次在 shell 中这样使用它,它会完全按预期工作:

$>sdev -s myservername
ssh -i ~/.ssh/id_rsa currentuser@myservername.devdomain.com

但是,如果我第二次运行它,而不指定 -s|--server,它将使用我上次运行时的服务器名称,似乎已经缓存了它:

$>sdev
ssh -i ~/.ssh/id_rsa currentuser@myservername.devdomain.com

它应该退出并出现错误并输出此消息:/bin/bash: A server name (-s|--server) is required.

这发生在任何参数上;也就是说,如果我指定了一个参数,然后下次我不指定,此方法将使用上次提供的参数。

显然,这不是我想要的行为。我的脚本中有什么责任这样做,我该如何解决?

#!/bin/bash

sdev() {
    getopt --test > /dev/null

    if [[ $? -ne 4 ]]; then
        echo "`getopt --test` failed in this environment"
        exit 1
    fi

    OPTIONS=u:,k:,p,s:
    LONGOPTIONS=user:,key:,prod,server:

    # -temporarily store output to be able to check for errors
    # -e.g. use “--options” parameter by name to activate quoting/enhanced mode
    # -pass arguments only via   -- "$@"   to separate them correctly
    PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTIONS --name "$0" -- "$@")

    if [[ $? -ne 0 ]]; then
        # e.g. $? == 1
        #  then getopt has complained about wrong arguments to stdout
        exit 2
    fi

    # read getopt’s output this way to handle the quoting right:
    eval set -- "$PARSED"

    domain=devdomain
    user="$(whoami)"
    key=id_rsa

    # now enjoy the options in order and nicely split until we see --
    while true; do
        case "$1" in
            -u|--user)
                user="$2"
                shift 2
                ;;
            -k|--key)
                key="$2".pem
                shift 2
                ;;
            -p|--prod)
                domain=proddomain
                shift
                ;;
            -s|--server)
                server="$2"
                shift 2
                ;;
            --)
                shift
                break
                ;;
            *)
                echo "Programming error"
                exit 3
                ;;
        esac
    done

    if [ -z "$server" ]; then
        echo "$0: A server name (-s|--server) is required."
        kill -INT $$
    fi

    echo "ssh -i ~/.ssh/$key.pem $user@$server.$domain.com"
    ssh -i ~/.ssh/$key $user@$server.$domain.com
}

【问题讨论】:

    标签: bash shell getopt


    【解决方案1】:

    server 是一个全局 shell 变量,因此它在函数的运行之间共享(只要它们在同一个 shell 中运行)。也就是说,当您运行sdev -s myservername 时,它会将变量server 设置为“myservername”。稍后,当您只运行sdev 时,它会检查$server 是否为空,发现不是,然后继续使用它。

    解决方案:使用局部变量!实际上,最好将函数中使用的所有变量声明为本地变量;这样,您就不会冒着干扰其他试图使用相同变量名的东西的风险。我还建议避免使用全大写变量名(如 OPTIONSLONGOPTIONSPARSED)——有一堆全大写变量对 shell 和/或其他程序有特殊含义,如果您错误地使用其中之一,可能会导致奇怪的问题。

    无论如何,这是一个简单的解决方案:在脚本开头附近添加:

    local server=""
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-26
      • 1970-01-01
      • 2012-08-05
      • 1970-01-01
      • 2013-11-05
      • 2013-02-26
      相关资源
      最近更新 更多