标签: subshell

有没有办法编写一个bash函数来中止整个执行,无论它是如何被调用的?

我在我的bash函数中使用"exit 1"语句来终止整个脚本,它工作正常:

function func()
{
   echo "Goodbye"
   exit 1
}
echo "Function call will abort"
func
echo "This will never be printed"
Run Code Online (Sandbox Code Playgroud)

但后来我意识到,当调用它时它不起作用:

res=$(func)
Run Code Online (Sandbox Code Playgroud)

据我所知,我创建了一个子shell,"退出1"中止了子shell而不是主要的....

有没有办法编写一个中止整个执行的函数,无论它如何被调用?我只需要获得真正的返回值(由函数回应).

bash function exit subshell

70
推荐指数
2
解决办法
4万
查看次数

将多个命令传递到单个命令中

如何将多个命令的stdout传递给单个命令?

示例1:组合并排序所有三个echo命令的输出:

echo zzz; echo aaa; echo kkk
Run Code Online (Sandbox Code Playgroud)

期望的输出:

aaa
kkk
zzz
Run Code Online (Sandbox Code Playgroud)

示例2:重写以下内容,以便所有命令都使用管道在单个命令行中,而不重定向到临时文件:

setopt > /tmp/foo; unsetopt >> /tmp/foo; set >> /tmp/foo; sort /tmp/foo
Run Code Online (Sandbox Code Playgroud)

bash shell pipe process subshell

54
推荐指数
2
解决办法
4万
查看次数

42
推荐指数
7
解决办法
5万
查看次数

什么时候命令替换会产生比单独的相同命令更多的子shell?

昨天有人向我建议在bash中使用命令替换会导致产生不必要的子shell.该建议仅针对此用例:

# Extra subshell spawned
foo=$(command; echo $?)

# No extra subshell
command
foo=$?
Run Code Online (Sandbox Code Playgroud)

我认为这对于这个用例似乎是正确的.但是,快速搜索试图验证这会导致大量令人困惑和矛盾的建议.似乎流行的智慧说所有命令替换的使用都会产生一个子shell.例如:

命令替换扩展为命令的输出.这些命令在子shell中执行,其stdout数据是替换语法扩展的内容.(来源)

这看起来很简单,除非你继续挖掘,在这种情况下你会开始找到建议的参考,但事实并非如此.

命令替换不一定会调用子shell,在大多数情况下也不会.它唯一保证的是乱序评估:它只是首先评估替换中的表达式,然后使用替换结果评估周围的语句.(来源)

这似乎是合理的,但这是真的吗?这个与子shell相关的问题的回答让我觉得有点man bash需要注意:

管道中的每个命令都作为单独的进程执行(即,在子shell中).

这让我想到了主要问题.究竟是什么导致命令替换产生一个副本,这个子shell无论如何都不会被隔离地执行相同的命令?

请考虑以下情况并解释哪些情况会产生额外子shell的开销:

# Case #1
command1
var=$(command1)

# Case #2
command1 | command2
var=$(command1 | command2)

# Case #3
command1 | command 2 ; var=$?
var=$(command1 | command2 ; echo $?)
Run Code Online (Sandbox Code Playgroud)

这些对中的每一对都会产生相同数量的子壳来执行吗?POSIX与bash实现有区别吗?是否有其他情况下使用命令替换会产生一个子shell,在这种情况下运行同一组命令不会?

bash shell optimization subshell command-substitution

24
推荐指数
2
解决办法
2596
查看次数

使用subshel​​l和substring打击坏替换

一个人为的例子......给出了

FOO="/foo/bar/baz"
Run Code Online (Sandbox Code Playgroud)

这工作(在bash中)

BAR=$(basename $FOO) # result is BAR="baz"
BAZ=${BAR:0:1}       # result is BAZ="b"
Run Code Online (Sandbox Code Playgroud)

这不

BAZ=${$(basename $FOO):0:1} # result is bad substitution
Run Code Online (Sandbox Code Playgroud)

我的问题是哪个规则导致这个[子shell替换]评估不正确?如果有的话,在1跳中执行此操作的正确方法是什么?

string bash substitution subshell

19
推荐指数
3
解决办法
4万
查看次数

为什么ssh在没有-t的情况下等待我的子shell,并用-t杀死它们?

我有一个bash脚本start.sh,如下所示:

for thing in foo bar; do
    {
        background_processor $thing
        cleanup_on_exit $thing
    } &
done
Run Code Online (Sandbox Code Playgroud)

这就是我想要的:我运行start.sh,它以代码0退出,并且两个子shell在后台运行.每个子shell运行background_processor,当它退出时,它运行cleanup_on_exit.即使我退出我最初运行start.sh的终端(即使这是一个ssh连接),这也有效.

然后我尝试了这个:

ssh user@host "start.sh"
Run Code Online (Sandbox Code Playgroud)

这是有效的,除了start.sh退出后,ssh显然也等待子壳退出.我真的不明白为什么.一旦start.sh退出,子shell变成pid 1的子项,并且它们甚至没有被赋予tty ...所以我无法理解它们如何仍然与我的ssh连接相关联.

我后来试过这个:

ssh -t user@host "start.sh"
Run Code Online (Sandbox Code Playgroud)

现在进程有一个指定的伪tty.现在,我发现ssh一旦start.sh退出就会退出,但它也会杀死子进程.

我猜想在后一种情况下子进程被发送了SIGHUP,所以我这样做了:

ssh -t user@host "nohup start.sh"
Run Code Online (Sandbox Code Playgroud)

这实际上有效!所以,我有一个解决我的实际问题的方法,但我想在这里掌握SIGHUP/tty的微妙之处.

总之,我的问题是:

  1. 为什么ssh(没有-t)即使在start.sh退出后也等待子进程,即使它们有父pid 1?
  2. 为什么SSH(与-t)杀子进程,显然与一个SIGHUP,即使当我从一个终端运行它们,然后注销该终端不发生?

ssh bash tty subshell

15
推荐指数
1
解决办法
4732
查看次数

如何在不创建子shell的情况下将命令输出存储在变量中[Bas​​h <v4]

ksh有一个非常有趣的构造来做到这一点,详细解答如下:https://stackoverflow.com/a/11172617/636849

从Bash 4.0开始,有一个内置的mapfile内置命令可以解决这个问题:http: //www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html

但奇怪的是,它似乎不适用于进程替换:

foo () { echo ${BASH_SUBSHELL}; }
mapfile -t foo_output <(foo) # FAIL: hang forever here
subshell_depth=${foo_output[0]} # should be 0
Run Code Online (Sandbox Code Playgroud)

但是如何在Bash v3.2中做到这一点?

bash variable-assignment subshell

15
推荐指数
2
解决办法
3996
查看次数

Jenkins管道sh似乎不尊重shell命令中的管道

我在版本2.32.2的管道中使用Jenkins文件.

由于各种原因,我想从pom中提取版本字符串.我希望我不必添加maven帮助插件并使用evaluate.

我很快想出了一个小的sed表达式,将它从使用管道的pom中取出,并在执行程序的jenkins工作区中的命令行上运行.

$ sed -n '/<version>/,/<version/p' pom.xml | head -1 | sed 's/[[:blank:]]*<\/*version>//g' 1.0.0-SNAPSHOT

它可能会被优化,但我想理解为什么管道似乎在管道sh命令失败.我玩过各种字符串格式,目前正在使用一个美元的字符串.

管道步骤如下所示,以便轻松输出命令字符串:

script {
    def ver_script = $/sed -n '/<version>/,/<version/p' pom.xml | head -1 | sed 's/[[:blank:]]*<\/*version>//g'/$
    echo "${ver_script}"
    POM_VERSION = sh(script: "${ver_script}", returnStdout: true)
    echo "${POM_VERSION}"
}
Run Code Online (Sandbox Code Playgroud)

当在jenkins管道中运行时,我得到以下控制台输出,它似乎将管道命令分离为单独的命令:

[Pipeline] script
[Pipeline] {
[Pipeline] echo
sed -n '/<version>/,/<version/p' pom.xml | head -1 | sed 's/[[:blank:]]*<\/*version>//g'
[Pipeline] sh
[FRA-198-versioned-artifacts-44SD6DBQOGOI54UEF7NYE4ECARE7RMF7VQYXDPBVFOHS5CMSTFLA] Running shell script
+ sed -n /<version>/,/<version/p pom.xml
+ head -1
+ sed s/[[:blank:]]*<\/*version>//g
sed: couldn't write …
Run Code Online (Sandbox Code Playgroud)

pipe subshell jenkins jenkins-pipeline

14
推荐指数
2
解决办法
1万
查看次数

通过管道从子shell获取退出代码

如何wget从子shell进程获取退出代码?

所以,主要问题是$?等于0.哪里可以$?=8建立?

$> OUT=$( wget -q "http://budueba.com/net" | tee -a "file.txt" ); echo "$?"
0
Run Code Online (Sandbox Code Playgroud)

tee实际上它没有用.

$> OUT=$( wget -q "http://budueba.com/net" ); echo "$?"
8
Run Code Online (Sandbox Code Playgroud)

但是${PIPESTATUS}数组(我不确定它与那种情况有关)也不包含该值.

$> OUT=$( wget -q "http://budueba.com/net" | tee -a "file.txt" ); echo "${PIPESTATUS[1]}"    

$> OUT=$( wget -q "http://budueba.com/net" | tee -a "file.txt" ); echo "${PIPESTATUS[0]}"
0

$> OUT=$( wget -q "http://budueba.com/net" | tee -a "file.txt" ); echo "${PIPESTATUS[-1]}"
0
Run Code Online (Sandbox Code Playgroud)

所以,我的问题是 - 如何wget通过tee子shell …

bash pipe exit-code tee subshell

13
推荐指数
2
解决办法
7692
查看次数

为什么要避免使用子壳呢?

我已经看到很多关于Stack Overflow的答案和评论,提到做一些事情来避免子shell.在某些情况下,给出了一个功能上的原因(通常,可能需要读取在其中分配的子shell之外的变量),但在其他情况下,避免似乎本身就被视为目的.例如

为什么是这样?是为了风格/优雅/美丽?为了表现(避免分叉)?为了防止可能的错误?别的什么?

bash subshell

13
推荐指数
1
解决办法
2256
查看次数