【问题标题】:Can't exit and strange order of execution with tee无法退出和奇怪的执行顺序与 tee
【发布时间】:2018-10-16 22:48:47
【问题描述】:

将函数的输出传递给tee 命令时,我遇到了一些奇怪的行为。第一个问题是,当从管道传输到tee 的函数调用exit 命令时,我无法退出程序。例如:

myfunction(){
    # Some stuff here
    exit 1
}

myfunction | tee -a $UPGRADE_LOG

当我运行上述代码时,程序无法退出并运行完成。

我遇到的另一个问题是tee 似乎导致某些代码以这样一种方式运行,以至于顺序顺序被撤消。我有以下输出:

SHOWING SYSTEM-WIDE AND INSTATNCE MEMORY USAGE:
Are you sure you would like to back up the instance given current memory contraints? [y/n]: Filesystem                           Size   Used  Avail  Use%  Mounted on
/dev/mapper/system-root              15G    13G   1.5G   90%   /
Log File Size: 24K   Total Size to Package: 248K Available Space: 1.5G

什么时候应该运行:

SHOWING SYSTEM-WIDE AND INSTATNCE MEMORY USAGE:
Filesystem                           Size   Used  Avail  Use%  Mounted on
/dev/mapper/system-root              15G    13G   1.5G   90%   /
Log File Size: 24K   Total Size to Package: 248K Available Space: 1.5G
Are you sure you would like to back up the instance given current memory contraints? [y/n]: 

不使用tee 时一切正常。这些问题似乎相互关联。有什么想法为什么会出现这种情况以及我应该怎么做?

【问题讨论】:

  • 请不要问多个问题,而是将它们分开。另请注意,如果您尽可能接近 MCVE,则会增加获得好答案的机会:stackoverflow.com/help/mcve
  • 这些问题好像和同一个问题有关,所以我把它们放在一起。很抱歉,如果它带来任何混乱。
  • 不,代码不会以非顺序方式运行 - 发生的情况是您的 stdout 和 stderr 正在经历不同的进程,因此它们在不同的时间到达。不过,原始写入仍然同时发生。
  • 是的,我知道代码是按顺序运行的。我只是指出它表现得如此。我认为这是因为它们是分开运行的,我只是不知道为什么。感谢您的澄清!

标签: bash tee


【解决方案1】:

kvantour's answer 告诉你为什么会发生这种情况。这个告诉你如何解决它。 :)

with_logs_piped() {
  local logfile=$1; shift
  "$@" > >(tee -a -- "$logfile") 2>&1  # 2>&1 redirects stderr through the same tee so it's
}                                      # ...also logged, and shows up sync'd with stdout.

myfunction() {
    # Some stuff here
    exit 1
}

with_logs_piped "$UPGRADE_LOG" myfunction

这里重要的是,我们不是使用常规管道,而是使用process substitution 代替tee——所以myfunction 在你的shell 本身中运行,而不是在子shell 中运行,所以exit 可以正确应用.


至于为什么通过tee重定向stdout会使stderr去同步,见Separately redirecting and recombining stderr/stdout without losing ordering

【讨论】:

  • 此解决方案运行良好。也感谢您的解释!
【解决方案2】:

exit 语句退出进程正在运行的(子)shell。现在惊喜来了:

管道

管道是由控制运算符||& 之一分隔的一个或多个命令的序列。管道的格式为:

[time [-p]] [ ! ] command [ [|⎪|&] command2 ... ]

管道中的每个命令都作为单独的进程执行(即在子外壳中)。

来源:man bash

因此,函数中的exit 语句只会杀死管道的子shell。这实际上意味着exit 在管道中什么都不做

$ exit | exit | echo foo
foo
$ exit | exit
$ # shell not terminated

注意:这显然取决于 shell,因为 的行为不同。

【讨论】:

  • “什么都不做”并不相当准确; exit 2 | exit 3 | true${PIPESTATUS[0]} 设置为 2 并将 ${PIPESTATUS[1]} 设置为 3,如果 set -o pipefail 处于活动状态,则将导致管道作为一个整体的退出状态为 3。尽管如此,这些效果并不是什么OP想要。 :)
  • @CharlesDuffy 是的。此外,如果设置了选项lastpipe,它将影响最后一个exit 语句(例如exit | exit
猜你喜欢
  • 2011-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-06
  • 2021-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多