这是 bash 中的错误吗?如果从管道调用,`return` 不会退出函数

Ter*_*ior 17 bash pipe shell-script subshell

我最近在使用 bash 时遇到了一些奇怪的问题。在尝试简化我的脚本时,我想出了一小段代码:

$ o(){ echo | while read -r; do return 0; done; echo $?;}; o
0
$ o(){ echo | while read -r; do return 1; done; echo $?;}; o
1
Run Code Online (Sandbox Code Playgroud)

return应该退出函数而不打印$?,不是吗?好吧,然后我检查了我是否可以单独从管道返回:

$ echo | while read -r; do return 1; done
bash: return: can only `return' from a function or sourced script
Run Code Online (Sandbox Code Playgroud)

没有while循环也会发生同样的情况:

$ foo(){ : | return 1; echo "This should not be printed.";}
$ foo
This should not be printed.
Run Code Online (Sandbox Code Playgroud)

有什么我在这里想念的吗?谷歌搜索对此一无所获!我的 bash 版本是Debian Wheezy 上的4.2.37(1)-release

jll*_*gre 17

这不是错误,bash而是其记录在案的行为

管道中的每个命令都在其自己的子 shell 中执行

return指令在函数定义内有效,但在子shell 中也是有效的,它不会影响其父shell,因此下一条指令 ,echo无论如何都会执行。尽管如此,它还是一种不可移植的 shell 结构,因为POSIX 标准允许组成管道的命令在子 shell(默认)或顶部(允许的扩展)中执行。

另外,多命令管道的每个命令都在一个子shell环境中;然而,作为扩展,管道中的任何或所有命令都可以在当前环境中执行。所有其他命令都应在当前 shell 环境中执行。

希望您可以bash通过以下几个选项告诉自己的行为方式:

$ set +m # disable job control
$ shopt -s lastpipe # do not run the last command of a pipeline a subshell 
$ o(){ echo | while read -r; do return 0; done; echo $?;}
$ o
$          <- nothing is printed here
Run Code Online (Sandbox Code Playgroud)

  • 我在文档中没有看到任何地方说子外壳内的 *return* 是有效的。我敢打赌这个功能是从 ksh 复制的,函数外部的 return 语句或源脚本的行为类似于 *exit*。我不确定原始的 Bourne shell。 (2认同)

yae*_*shi 10

相关:https : //stackoverflow.com/a/7804208/4937930

不能通过子shellexitreturn在子shell 中退出脚本或从函数返回,这不是一个错误。它们在另一个进程中执行,不会影响主进程。

除此之外,我想您会在(可能)未定义的规范上看到 bash 的未记录行为。在一个函数中,return在子shell命令的顶层没有断言错误,它的行为就像exit.

恕我直言,这是一个 bash 错误,因为return根据主语句是否在函数中的行为不一致。

#!/bin/bash

o() {
    # Runtime error, but no errors are asserted,
    # each $? is set to the return code.
    echo | return 10
    echo $?
    (return 11)
    echo $?

    # Valid, each $? is set to the exit code.
    echo | exit 12
    echo $?
    (exit 13)
    echo $?
}
o

# Runtime errors are asserted, each $? is set to 1.
echo | return 20
echo $?
(return 21)
echo $?

# Valid, each $? is set to the exit code.
echo | exit 22
echo $?
(exit 23)
echo $?
Run Code Online (Sandbox Code Playgroud)

输出:

$ bash script.sh 
10
11
12
13
script.sh: line 20: return: can only `return' from a function or sourced script
1
script.sh: line 22: return: can only `return' from a function or sourced script
1
22
23
Run Code Online (Sandbox Code Playgroud)


cuo*_*glm 6

根据 POSIX 文档,未指定使用return外部函数或源脚本。所以,这取决于你的外壳来处理。

SystemV外壳会报告错误,而在kshreturn功能执行的脚本外的行为或类似exit。大多数其他 POSIX shell 和schily 的 osh也有类似的行为:

$ for s in /bin/*sh /opt/schily/bin/osh; do
  printf '<%s>\n' $s
  $s -c '
    o(){ echo | while read l; do return 0; done; echo $?;}; o
  '
done
</bin/bash>
0
</bin/dash>
0
</bin/ksh>
</bin/lksh>
0
</bin/mksh>
0
</bin/pdksh>
0
</bin/posh>
0
</bin/sh>
0
</bin/yash>
0
</bin/zsh>
</opt/schily/bin/osh>
0
Run Code Online (Sandbox Code Playgroud)

ksh并且zsh没有输出,因为这些shell中管道的最后一部分是在当前shell而不是子shell中执行的。return 语句影响了调用该函数的当前 shell 环境,导致函数立即返回而不打印任何内容。

在交互会话中,bash只报错但不终止shell,schily's osh报错并终止shell:

$ for s in /bin/*sh; do printf '<%s>\n' $s; $s -ci 'return 1; echo 1'; done
</bin/bash>
bash: return: can only `return' from a function or sourced script
1
</bin/dash>
</bin/ksh>
</bin/lksh>
</bin/mksh>
</bin/pdksh>
</bin/posh>
</bin/sh>
</bin/yash>
</bin/zsh>
</opt/schily/bin/osh>
$ cannot return when not in function
Run Code Online (Sandbox Code Playgroud)

zsh在交互式会话和输出是终端不终止bashyashschily's osh报告错误但没有终止外壳)

  • 我觉得不行。那是在函数之外。调用函数的shell和执行*return*的子shell是不同的。 (3认同)
  • 我的意思是在一个本身在函数内的子外壳内并不一定意味着在该函数之外,即标准状态管道组件中的任何内容都不会被认为是在它们所在的函数之外。这应该由开放组加以澄清。 (2认同)