设置 shell 变量然后调用子进程失败

设置 shell 变量然后调用子进程失败

我正在使用 getopts,它有一个OPTERR影响其行为的 shell 变量。我想在同一行更改 getopts 的值OPTERR并调用 getopts ,以影响 getopts 的行为,但OPTERR此后恢复为其默认值。因此,考虑到我对混合变量更改命令调用的过程有点模糊,我想我应该尝试一个简单的echo.这是一系列 shell 命令及其输出,其中一些对我来说没有意义。

$ echo OPTERR
1
$ OPTERR=0 echo $OPTERR
1

现在,我们就到此为止吧。第二个命令的输出不应该是“0”吗?所以在问这个问题之前,我绞尽脑汁,认为这一定是因为以下命令应该在子 shell 中。所以后来我尝试了...

$ OPTERR=0 (echo $OPTERR)
-bash: syntax error near unexpected token `('

这更是一个更大的惊喜。如果上述构造存在语法错误,如何更改 shell 变量的值以运行子 shell? (我还尝试在 echo 命令后放置一个终端“;”,得到相同的结果)。我也尝试过...

$OPTERR=0 bash -c 'echo $OPTERR'
1

所以,那里也没有欢乐。我采取了不同的策略...

$function sf() { echo $OPTERR }
$sf
1
$OPTERR=0 sf
0
$sf
1

成功!!但为什么?函数特别不是子 shell(我不这么认为),这并不是我真正想要的(尽管,我可能可以让它适用于我的应用程序)。这是另一个有效的公式(某种程度上):

$function sf() { (OPTERR=0; echo $OPTERR;) }
$sf
0
$echo $OPTERR
1

这非常接近我想要的,但是当然实际脚本(不是我的琐碎echo)的预期目标是getopts,并且getopts必须更改脚本其余部分可以看到的变量。因此,我将简单的echo脚本更改为简单的变量集,如......

$function sf() { foo='bar'; (foo='check'; echo $foo;); echo $foo; }
$sf
check
bar

噢!!所以我可以getopts使用我想要的环境进行调用,但我无法从子 shell 中获取任何值。我尝试导出 foo 和各种太尴尬的东西,无法在这里列出,但这是我的问题:如果你把我所有的实验放在一起,它们必须揭示一个根本错误的模型,即所有这一切应该如何工作。我的理解中缺少什么?正确的做法是什么?

只是重申一下,我想做的就是构建一个函数,该函数使用getoptswithOPTERR设置为 0,理想情况下无需在调用函数的行上进行设置,并且以在函数外部恢复的OPTERR方式进行设置,并且大多数OPTERR重要的是,以一种允许我实际处理getopts.这听起来好像有很多问题,但这是相当明智的,并且这个OPTERR变量的存在表明它应该是可能的。

答案1

$ echo $VAR

这给出了旧值,$VAR因为 shell 仅在命令行上扩展后才应用变量赋值。

IFS= read -r foo然而,您经常在网站上看到的构造是有效的,因为它read不是IFS从命令行获取,而是在内部使用它。同样,OPTERR=0 getopts ...应该也有效。

$ cat arg.sh
#!/bin/bash
OPTERR=0 getopts a:b var
echo "var: $var OPTERR: $OPTERR"

$ bash arg.sh -z
var: ? OPTERR: 1

即使-z选项无效,也不会出现错误消息。


$ OPTERR=0 bash -c 'echo $OPTERR'

这有点特殊,因为OPTERR很特殊。 Bash 的手册页显示:

OPTERR如果设置为值 1,Bash 将显示内置命令生成的错误消息getoptsOPTERR每次调用 shell 或执行 shell 脚本时都会初始化为 1。

不过,这个想法是正确的,它适用于常规变量:

$ FOO=123 bash -c 'echo $FOO'
123

我想做的就是构建一个函数,它使用getopts

如果您正在解析函数的参数,并从主程序中多次调用它(例如dothis -a; dothis -b -c),并且不使用该函数来解析 shell 的参数(parse_args "$@"),那么您还需要记住getopts修改OPTIND变量, 也。它还需要使其成为该函数的本地化。

例如,在这里,第二个函数调用看不到任何选项:

foo() { while getopts "abcd" opt; do echo "$opt"; done; };
foo -a -a
foo -b -b

与前一个结合起来,这个函数将默默地工作:

foo() {
    local OPTIND=1
    while OPTERR=0 getopts "abcd" opt; do
        echo "$opt"
    done
}

答案2

如果您需要在函数调用期间 OPTERR 为 0,请将其设置为局部变量,如 Gordon Davisson 所示:

#!/bin/bash

myfunc() {
  local OPTERR=0
  printf "Inside myfunc, OPTERR=%d\n" "$OPTERR"
  while getopts ":a:" opt; do
    echo $opt is $OPTARG
  done
}

printf "Before calling myfunc, OPTERR=%d\n" "$OPTERR"
myfunc
printf "After calling myfunc, OPTERR=%d\n" "$OPTERR"

结果是:

Before calling myfunc, OPTERR=1
Inside myfunc, OPTERR=0
After calling myfunc, OPTERR=1

相关内容