Bash 由于文件描述符和进程替换而导致的SIGPIPE

Bash 由于文件描述符和进程替换而导致的SIGPIPE,bash,file-descriptor,bash-trap,process-substitution,Bash,File Descriptor,Bash Trap,Process Substitution,我试图保存bash函数中的一些日志,这些函数执行工具(其中一些在子shell中运行)。此外,我想将所有错误打印到终端 在点击ctr-c和一个奇怪的日志文件时,我的代码会导致一个SIGPPIPE和退出代码141。管道故障似乎是由陷阱内的stdout重定向到stderr引起的,这会中断tee命令的stdout流。有趣的是,代码按预期终止,退出代码为130,没有陷阱中使用的重定向或cat命令 我仍然无法修复和解释生成的日志文件。为什么会有两次回音,为什么陷阱回音也会写入文件 为什么sigpipe不是

我试图保存bash函数中的一些日志,这些函数执行工具(其中一些在子shell中运行)。此外,我想将所有错误打印到终端

在点击ctr-c和一个奇怪的日志文件时,我的代码会导致一个SIGPPIPE和退出代码141。管道故障似乎是由陷阱内的stdout重定向到stderr引起的,这会中断tee命令的stdout流。有趣的是,代码按预期终止,退出代码为130,没有陷阱中使用的重定向或
cat
命令

  • 我仍然无法修复和解释生成的日志文件。为什么会有两次回音,为什么陷阱回音也会写入文件

  • 为什么sigpipe不是由函数内的重定向引起的

  • 日志文件

    fun\u stdout
    乐趣
    乐趣
    陷阱
    
    编辑:根据oguz ismail的工作示例回答

    exec 3>> log
    exec 4> >(tee -ai log >&2)
    fun 2>&4 >&3
    exec 3>&-
    exec 4>&-
    

    SIGPIPE的来源是SIGINT(由ctrl/c启动)被发送到所有正在运行的进程:“main”bash进程(执行“fun”函数)和执行“tee-a”的子shell。结果,在Ctrl/C上,两个都会被杀死。当主进程尝试将“trap_stderr”发送到te“tee”进程时,它会得到SIGPIPE,因为“tee”已经死了

    考虑到“tee-a”的角色,保护它不受SIGINT的影响,并允许它运行直到“fun”完成(或被杀死)。考虑到最后一行

    的以下更改
    fun >> log 2> >(trap '' INT ; tee -a log >&2)
    
    这将生成日志文件:

    Console (stderr)
    fun_stderr
    ^Ctrap_stderr
    
    Log File: (no duplicates)
    
    fun_stdout
    fun_stderr
    trap_stdout
    trap_stderr
    
    以上内容还将解决第二个问题,即关于日志文件中的重复行。这是使用tee将每个stderr行发送到日志文件和stdout的结果。假设stdout刚刚被重定向(通过'>>log')到'log'文件,那么输出的两个副本都被发送到日志文件,没有一个发送到终端

    如果按顺序执行重定向,则更改“T”行以将输出发送到原始stderr(而不是已重定向的stdout)将在终端上显示输出(或任何stderr)

    为什么会有两次回声

    fun
    的stdout在其stderr重定向到为
    tee
    创建的FIFO之前被重定向到
    log
    ,因此
    tee
    继承重定向到
    log
    的stdout。我可以这样证明:

    $:>文件2>>(日期)
    $cat文件
    星期六2020年7月25日18:46:31+03
    
    改变重定向的顺序可以解决这个问题。例如:

    fun 2> >(tee -a log) >> log
    
    为什么陷阱回音也会写入文件

    如果为SIGINT设置的陷阱是在shell仍在执行
    fun
    时触发的,则与
    fun
    关联的重定向生效是完全正常的

    要将陷阱操作的stdout和stderr连接到主shell的stdout和stderr,可以执行以下操作:

    exec 3>&1 4>&2
    
    handler() {
      : # handle SIGINT here
    } 1>&3 2>&4
    
    trap handler INT
    
    或者类似的东西;这个想法是复制主shell的stdout和stderr

    为什么sigpipe不是由函数内的重定向引起的

    因为
    tee
    在执行
    echo-fun\u-stderr>&2
    时处于活动状态。而且
    sleep
    不会向其标准输出写入任何内容,因此它无法触发SIGPIPE

    此脚本由于SIGPIPE而终止的原因是
    tee
    也接收由键盘生成的SIGINT,并在执行与SIGINT关联的陷阱操作之前终止。因此,在执行
    echo trap\u stderr>&2
    时,由于其stderr连接到一个刚才已关闭的管道,因此外壳接收到SIGPIPE

    为了避免这种情况,如前所述,您可以使
    tee
    ignoresigint。不过,您不需要为此设置空陷阱,
    -i
    选项就足够了

    fun 2> >(tee -a -i log) >> log
    
    fun 2> >(tee -a -i log) >> log