Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/bash/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如果后跟管道,Linux tee命令有时会失败_Linux_Bash_Tee - Fatal编程技术网

如果后跟管道,Linux tee命令有时会失败

如果后跟管道,Linux tee命令有时会失败,linux,bash,tee,Linux,Bash,Tee,我使用tee log和xargs进程输出运行find命令;无意中,我忘了在第二个管道中添加xargs,并发现了这个问题 例如: % tree . ├── a.sh └── home └── localdir ├── abc_3 ├── abc_6 ├── mydir_1 ├── mydir_2 └── mydir_3 7 directories, 1 file 而a.sh的内容是: % cat a.sh

我使用tee log和xargs进程输出运行find命令;无意中,我忘了在第二个管道中添加
xargs
,并发现了这个问题

例如:

% tree
.
├── a.sh
└── home
    └── localdir
        ├── abc_3
        ├── abc_6
        ├── mydir_1
        ├── mydir_2
        └── mydir_3

7 directories, 1 file
a.sh
的内容是:

% cat a.sh
#!/bin/bash
LOG="/tmp/abc.log"

find home/localdir -name "mydir*" -type d  -print | tee $LOG | echo
如果我用一些命令添加第二个管道,例如
echo
ls
,写入日志操作偶尔会失败

以下是我多次运行
/a.sh
时的一些示例:

% bash -x ./a.sh; cat /tmp/abc.log  // this tee failed
+ LOG=/tmp/abc.log
+ find home/localdir -name 'mydir*' -type d -print
+ tee /tmp/abc.log
+ echo


% bash -x ./a.sh; cat /tmp/abc.log  // this tee ok
+ LOG=/tmp/abc.log
+ find home/localdir -name 'mydir*' -type d -print
+ tee /tmp/abc.log
+ echo

home/localdir/mydir_2  // this is cat /tmp/abc.log output
home/localdir/mydir_3
home/localdir/mydir_1

为什么如果我用某个命令添加第二个管道(并且忘记
xargs
),则
tee
命令偶尔会失败?

问题是,默认情况下,当写入管道失败时,
tee
退出。因此,考虑一下:

find home/localdir -name "mydir*" -type d  -print | tee $LOG | echo
如果
echo
首先完成,管道将失败,
tee
将退出。不过,时间安排并不精确。管道中的每个命令都位于单独的子shell中。此外,缓冲也有变幻莫测的地方。因此,有时日志文件是在退出之前写入的,有时则不是

为了清楚起见,让我们考虑一个更简单的管道:

$ seq 10 | tee abc.log | true; declare -p PIPESTATUS; cat abc.log
declare -a PIPESTATUS='([0]="0" [1]="0" [2]="0")'
1
2
3
4
5
6
7
8
9
10
$ seq 10 | tee abc.log | true; declare -p PIPESTATUS; cat abc.log
declare -a PIPESTATUS='([0]="0" [1]="141" [2]="0")'
$
在第一次执行中,管道中的每个进程都以成功状态退出,并写入日志文件。在同一命令的第二次执行中,
tee
失败,退出代码
141
,日志文件未写入

我用
true
代替
echo
来说明
echo
没有什么特别之处。任何可能拒绝输入的
tee
之后的命令都存在问题

文档 最新版本的
tee
具有控制管道故障退出行为的选项。从coreutils-8.25的
man tee

--输出错误[=模式]
设置写入错误的行为。请参阅下面的模式

模式的可能性包括:

模式确定输出上有写入错误的行为:

   'warn' diagnose errors writing to any output

   'warn-nopipe'
          diagnose errors writing to any output not a pipe

   'exit' exit on error writing to any output

   'exit-nopipe'
          exit on error writing to any output not a pipe
-p选项的默认模式是“warn nopipe”。默认值 未指定--output error时的操作是立即退出 在写入管道时出错,并诊断写入非管道时出错 产出

如您所见,默认行为是“立即退出”
写入管道时出错”。因此,如果在
tee
写入日志文件之前尝试写入
tee
之后的进程失败,那么
tee
将在不写入日志文件的情况下退出。

我调试了
tee
源代码,但我不熟悉Linux C,因此可能会出现问题

tee
属于coreutils包,位于
src/tee.c

首先,它使用以下设置缓冲区:

setvbuf (stdout, NULL, _IONBF, 0); // for standard output
setvbuf (descriptors[i], NULL, _IONBF, 0);  // for file descriptor
那么它是无缓冲的

第二,tee将stdout作为描述符数组中的第一个项,并将使用for循环写入描述符:

/* In the array of NFILES + 1 descriptors, make
   the first one correspond to standard output.   */
descriptors[0] = stdout;
files[0] = _("standard output");
setvbuf (stdout, NULL, _IONBF, 0);

...

  for (i = 0; i <= nfiles; i++) {
    if (descriptors[i]
        && fwrite (buffer, bytes_read, 1, descriptors[i]) != 1)  // failed!!!
      {
        error (0, errno, "%s", files[i]);
        descriptors[i] = NULL;
        ok = false;
      }
    }

对,从T形三通到提前退出的某物的管道(在您的案例中不依赖于从T形三通读取输入)将导致间歇性错误。 有关此问题的摘要,请参阅:


为什么要在管道后添加回声。。我认为tee已经可以正常工作了。@LeeHoYo echo只是一个例子,我用xargs运行这个命令,但由于偶然的原因,我忘记了写xargs并发现了这个问题,所以我想知道为什么会导致这个问题。@TankyWoo,但是echo不读取
stdin
,所以一旦管道充满,它就会阻塞所有管道(或者如果使用echo internal bash命令,可能会出现错误,因为echo不读取任何内容)@LuisColorado阻塞管道是结果之一,例如将
echo
替换为
sleep 1
,但它可以写入文件。请参阅我的答案。@TankyWoo,如果将
echo
替换为
sleep 1
(也不从标准输入数据中读取数据)如前所述,一旦完成,内核将向其提供输入的进程发出信号,并返回一条
断管
消息。写入
echo
的标准输入不会失败,直到
echo
退出并且
tee
尝试再次写入。
echo
可能会很快退出在写入标准输出之前,
tee
可能会花时间等待输入。@JonathanLeffler OK。这很可能在这里,我更新了答案。我还测试了
seq 100000 | tee abc.log | sleep 10
,并获得了相同的间歇性结果,即使
sleep
是长期存在的。在这种情况下,我觉得当睡眠输入缓冲区填满时,即使睡眠仍处于活动状态,tee也不能再写入,这会触发失败。当管道填满时,
tee
将被阻止再写入管道(在上次
write
调用中被阻止),直到“读取”过程(
sleep
echo
,两者均未实际读取其标准输入)读取某些内容,或终止(或以其他方式关闭其标准输入,因此没有剩余进程读取
tee
的标准输出)@TankyWoo感谢您提供有关输出错误和coreutils版本的信息。答案已更新。至于管道,在Unix上,无论如何,不,进程不是连续运行的:它们是并行运行的。当使用管道提供连续输出时,这一点很重要。它还允许管道处理大量数据,而无需将数据写入每一步都使用磁盘。@TankyWoo:No-管道不是序列化执行。管道使用程序的并发执行。它必须;管道的容量有限且非常小(传统上为5 KiB,但在现代系统上通常为64 KiB),因此,如果流经管道的数据超过该大小,则程序必须同时执行,否则写入过程将被阻止。我很想对此投反对票,因为这基本上是一种转移视线的行为,但最后的见解是,这是一种浪费精力的行为,这是很有价值的,我相信这是出于好意而进行的.
write(1, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n", 21) = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=22649, si_uid=1000} ---
+++ killed by SIGPIPE +++