bash - 重定向 ${var :? } 构造中的标准错误

标签 bash io-redirection

来自 man bash :

${parameter:?word}
If parameter is null or unset, the expansion of word (or a message to that effect if word is not present) is written to the standard error and the shell, if it is not interactive, exits. Otherwise, the value of parameter is substituted.

那么为什么我不能将错误重定向到 /dev/null(或其他任何地方):

$ ${a:?} 2> /dev/null
bash: a: parameter null or not set

echo 都没有:

$ echo ${a:?} 2> /dev/null
bash: a: parameter null or not set
$ echo Hello world ${a:?} 2> /dev/null
bash: a: parameter null or not set

我的 bash 版本是 4.3.42


更新:

为了不转移我的问题,我需要说 a 没有定义而且永远不会,所以我的问题在定义时不适用,因此我不关心是否a 扩展为不是命令、别名等的内容...

考虑这个例子不要混淆:

$ echo ${a:=1} ${b:?} ${c:=3}
bash: b: parameter null or not set
$ echo "a: $a - c: $c"
a: 1 - c: 

最佳答案

了解 shell 解释命令的顺序很重要。

这是由 Posix standard, volume XSH, section 2.1 指定的。 (包括 bash 在内的各种 shell 对此过程进行了扩展,但基本原理没有改变):

  1. The shell reads its input from a file (see sh), from the -c option or from the system() and popen() functions defined in the System Interfaces volume of POSIX.1-2008. If the first line of a file of shell commands starts with the characters "#!", the results are unspecified.
  2. The shell breaks the input into tokens: words and operators; see Token Recognition.
  3. The shell parses the input into simple commands (see Simple Commands) and compound commands (see Compound Commands).
  4. The shell performs various expansions (separately) on different parts of each command, resulting in a list of pathnames and fields to be treated as a command and arguments; see wordexp.
  5. The shell performs redirection (see Redirection) and removes redirection operators and their operands from the parameter list.
  6. The shell executes a function (see Function Definition Command), built-in (see Special Built-In Utilities), executable file, or script, giving the names of the arguments as positional parameters numbered 1 to n, and the name of the command (or in the case of a function within a script, the name of the script) as the positional parameter numbered 0 (see Command Search and Execution).
  7. The shell optionally waits for the command to complete and collects the exit status (see Exit Status for Commands).

这里,相关部分是第 4 步到第 6 步。当我们到达第 4 步时,shell 已经识别出它将要运行的命令。然后它执行指示的各种扩展,包括 ${a:?}。此时,我们还没有开始执行命令,也没有做任何重定向。

在第 5 步中,重定向被执行并从命令行中删除。 (从左到右)。

在第 6 步中,命令名称被识别(因为它可能依赖于第 4 步的扩展),该名称被识别为特殊的内置程序、别名或外部程序——在这种情况下找到可执行文件——最后命令实际运行,在第 5 步中执行的重定向适用的环境中。

因此重定向 stderr 不适用于参数扩展。参数扩展期间产生的任何错误消息都会发送到 shell 环境的当前标准错误。

构造 { command-list } 是一个复合命令。如 Posix XSH section 2.9.4 所示:(已添加高亮显示):

The shell has several programming constructs that are "compound commands", which provide control flow for commands. Each of these compound commands has a reserved word or control operator at the beginning, and a corresponding terminator reserved word or operator at the end. In addition, each can be followed by redirections on the same line as the terminator. Each redirection shall apply to all the commands within the compound command that do not explicitly override that redirection.

因此应用于复合命令的重定向适用于复合命令的持续时间,因此包括复合命令执行期间 shell 的输出。

值得注意的是:

${a:=1} ${b:?} ${c:=3}

不正确。如果 $b 有一个值——比如“B”——,那么命令的执行不会被中断,那么要执行的命令将是 1,因为扩展的结果是:

1 B 3

(假设 ac 由默认赋值结构赋值)。这将尝试运行命令 1,为其提供参数 B3。由于这发生在第 5 步应用重定向之后,错误消息 (1: not found) 将被重定向,这意味着如果您已将 stderr 重定向到 /dev/null

如果你想使用条件扩展执行一个或多个参数设置,你应该使用:(无操作)命令:

: ${a:=1} ${b:?} ${c:=3}

最后,参数扩展过程中的错误很可能会中断命令执行,这是正确的。如果命令行出现在脚本中(因此 shell 不是交互式的),则 ${b:?} 的失败会导致“shell 以非零退出状态退出”(从 Posix XSH section 2.6.2 ,以及在 OP 中引用的 bash 手册中),这将使所有变量设置无效,因为它们对于正在执行的 shell 是本地的。 XSH 2.6.2 继续说“交互式 shell 不需要退出”,实际上 bashdash 交互式 shell 都不会退出。但是错误就是错误,它会中断命令执行。 Posix XSH section 2.8.2 建议应该是这种情况:

If a command fails during word expansion or redirection, its exit status shall be greater than zero.

虽然没有明确说明命令执行被错误中断,但它似乎是一个明确的指示,因为错误已经设置了命令的退出状态。我试过的 shell 确实做到了这一点。

关于bash - 重定向 ${var :? } 构造中的标准错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32808703/

相关文章:

python - 删除冒号后的数字

bash - 在 Bash 中创建互斥锁时,我应该使用 `flock` 还是 `mkdir` ?

bash - 在 Bash 中重定向 stderr 和 stdout

linux - 将关联数组作为 Bash 的参数传递

bash - 使用 bash 解析 .env 文件并将其注入(inject)命令行

bash - 为什么 sed replace + redirect 删除了我的文件?

c++ - 如何从 std::cin 读取直到流结束?

c - 在 Unix 中如何处理 '<'?

bash - 重定向的意外输出

Bash:将带有空变量的 "$@"参数传递给 sudo -i