简体   繁体   English

当bash子脚本与父脚本一起以交互方式(由终端)调用时退出,而不是由cron以非交互方式被调用时退出

[英]bash child script exits along with parent script when parent invoked interactively / by terminal, but not when invoked non-interactively / by cron

This is parent.sh: 这是parent.sh:

#!/bin/bash

trap 'exit' SIGHUP SIGINT SIGQUIT SIGTERM

if ! [ -t 0 ]; then # if running non-interactively
    sleep 5 & # allow a little time for child to generate some output
    set -bm # to be able to trap SIGCHLD
    trap 'kill -SIGINT $$' SIGCHLD # when sleep is done, interrupt self automatically - cannot issue interrupt by keystroke since running non-interactively
fi

sudo ~/child.sh

This is child.sh: 这是child.sh:

#!/bin/bash

test -f out.txt && rm out.txt

for second in {1..10}; do
    echo "$second" >> out.txt
    sleep 1
done

If run the parent script in a terminal like so... 如果在这样的终端中运行父脚本...

~/parent.sh

...and after about 3 seconds, issue an interrupt by keystroke. ...大约3秒钟后,通过按键发出一个中断。 When checking out.txt a few seconds later, it will look like... 几秒钟后检查out.txt时,它看起来像...

1  
2  
3  

...thus indicating that parent and child ended upon (keystroke) interrupt. ...因此指示父母和孩子在(按键)中断后结束。 This is corroborated by checking ps -ef in real-time and seeing that the script processes are present before the interrupt and gone after the interrupt. 通过实时检查ps -ef并查看脚本进程在中断之前存在并在中断之后消失,可以证实这一点。

If parent script is invoked by cron like so... 如果像cron这样调用父脚本,则...

* * * * * ~/parent.sh  

...the content of out.txt is always... ... out.txt的内容始终是...

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  

...thus indicating that at least the child did not end upon (kill command) interrupt. ...因此表明至少孩子没有在(kill命令)中断后结束。 This is corroborated by checking ps -ef in real-time and seeing that the script processes are present before the interrupt and only the parent process is gone after the interrupt, but the child process persists until it runs its course. 通过实时检查ps -ef并查看脚本进程是否在中断之前存在,并且只有父进程在中断之后消失,而子进程一直持续到运行过程,可以证实这一点。

Attempts to solve... 尝试解决...

  1. Shell options can only be a factor here, inasmuch as non-interactive invocations of parent run set -bm (which entails PGIDs of children differing from PGID of parent - relevant up ahead). Shell选项在这里仅是一个因素,因为父级运行set -bm非交互式调用(这意味着子级的PGID与父级的PGID不同-以后相关)。 Other than that, both scripts show only options hB enabled, whether running interactively or not. 除此之外,这两个脚本都仅显示启用了hB的选项,无论是否以交互方式运行。
  2. Looked thru man bash for clues but found nothing helpful. 通过男子扑寻求线索,但没有发现任何帮助。
  3. Tried a handful of web searches which included many results from stackoverflow, but while some were similar to this question, none were the same. 尝试了少数Web搜索,其中包括很多来自stackoverflow的结果,但是尽管其中一些与该问题相似,但没有一个是相同的。 The closest answers entailed... 最接近的答案是……
    • using wait to get the child process id and invoking kill on it - results in "/parent.sh: line 30: kill: (17955) - Operation not permitted" 使用等待获取子进程ID并对其调用死亡-导致“ /parent.sh:第30行:kill:(17955)-不允许操作”
    • invoking a kill on the process group - results in "~/parent.sh: line 31: kill: (-15227) - Operation not permitted" (kill using the PGID of child, which differs from parent when non-interactive, due to job control enabling) 在进程组上调用kill-导致“〜/ parent.sh:第31行:kill:(-15227)-不允许操作”(使用子进程的PGID进行终止,由于非交互方式,父进程不同于父进程的PGID,原因是启用工作控制)
    • looping thru the current jobs and killing each 遍历当前工作并杀死每个工作

Is the problem with these solutions that the parent runs as a regular user, while the child runs as root via sudo (it will ultimately be a binary, not a suid script), so the parent cannot kill it? 这些解决方案的问题是父级以常规用户身份运行,而子级通过sudo以root身份运行(最终将是二进制文件,而不是suid脚本),因此父级无法杀死它吗? If that's what "Operation not permitted" means, why is the sudo invoked process killable when sending a keystroke interrupt via terminal? 如果那是“不允许操作”的意思,那么当通过终端发送按键中断时,为什么sudo调用的进程会被杀死?

The natural course is to avoid additional code, unless necessary - ie since the scripts behave correctly when run interactively, if feasible it's much preferred to simply apply the same behavior when running non-interactively / by cron. 自然的做法是,除非有必要,否则避免使用其他代码-即由于脚本在交互运行时会正确运行,因此,如果可行的话,在以cron非交互方式运行时,简单地应用相同的行为是更为可取的。

The bottom line question is, what can be done to make an interrupt (or term) signal issued while running non-interactively, produce the same behavior as an interrupt signal issued when running interactively? 最重要的问题是,如何使非交互式运行时发出的中断(或术语)信号产生与交互式运行时发出的中断信号相同的行为?

Thanks. 谢谢。 Any help is greatly appreciated. 任何帮助是极大的赞赏。

  1. When you manually run the script from an interactive shell (usually running on a pty ), it's the terminal driver who catches CTRL-C and convert it to SIGINT and send to all processes in the foreground process group (the script itself and the sudo command). 当您从交互式外壳程序手动运行脚本(通常在pty上运行)时, 终端驱动程序会捕获CTRL-C并将其转换为SIGINT并发送给前台进程组中的所有进程 (脚本本身和sudo命令) )。
  2. When your script is running from cron you only send SIGINT to the shell script itself and the sudo command will continue running and bash will not kill its child when it exits for this kind of scenario. 当脚本从cron运行时,您仅将SIGINT发送到shell脚本本身,并且sudo命令将继续运行,并且bash在这种情况下退出时不会杀死其子级。

To explicitly send a signal to a whole process group you can use the negative process group ID . 要将信号显式发送到整个过程组,可以使用否定的过程组ID For your case the pgid should be the PID of the shell script so try like this: 对于您的情况,pgid应该是shell脚本的PID,因此请尝试如下操作:

trap 'kill -SIGINT -$$' SIGCHLD

UPDATE: 更新:

It turns out my assumption about the value of pgid is wrong. 事实证明,我对pgid的值的假设是错误的。 Just did a test with this simple cron.sh : 只是用这个简单的cron.sh做了一个测试:

#!/bin/bash
set -m
sleep 888 &
sudo sleep 999

and crontal -l looks like this: crontal -l看起来像这样:

30 * * * * /root/tmp/cron.sh

When the cron job is running the ps outputs like this: 当cron作业正在运行时, ps输出如下:

 PPID    PID   PGID    SID   COMMAND
15486  15487  15487  15487   /bin/sh -c /root/tmp/cron.sh
15487  15488  15487  15487   /bin/bash /root/tmp/cron.sh
15488  15489  15489  15487   sleep 888
15488  15490  15490  15487   sudo sleep 999
15490  15494  15490  15487   sleep 999

So the sudo (and its child) is running in a separate pgrp and the pgid is not the pid of the cron.sh so my solution ( kill -INT -$$ ) would not work. 所以sudo (及其子)在单独的PGRP正在运行,并且PGID不是的PID cron.sh所以我的解决方案( kill -INT -$$ )是行不通的。

Then I think we can solve the problem like this: 然后,我认为我们可以解决以下问题:

#!/bin/bash
set -m
sudo sleep 999 & # run sudo in backgroup
pid=$!           # save the pid which is also the pgid
sleep 5
sudo kill -INT -$pid  # kill the pgrp.
                      # Use sudo since we're killing root's processes

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 仅当非交互式运行bash脚本时,出现错误“猫:写入错误:管道损坏” - Getting error “cat: write error: Broken pipe” only when running bash script non-interactively 通过cron调用时,MQTT Python无法启动Bash脚本 - MQTT Python unable to start Bash script when invoked via cron python - 从bash调用脚本时终止子进程 - python - terminate child process when script invoked from bash 从SecureCRT调用时,脚本在调用tput的行上退出 - Script exits on line calling tput when invoked from SecureCRT 如何在shell或Perl脚本中发出“模块加载”(即非交互式) - How to issue “module load” in a shell or Perl script (i.e., non-interactively) 以非交互方式运行 rustup 的 curl-fetched 安装程序脚本 - run rustup's curl-fetched installer script non-interactively 在bash脚本中将命令抛出到调用的终端中 - Throw commands inside invoked terminal in bash script 父母退出时,叉子进程死亡? (重击) - Fork child process to die when parent exits? (bash) 在另一个脚本中运行bash脚本时,父脚本中定义的所有变量是否都继承给孩子? - When running a bash script within another script, are all variables defined in the parent script inherited to the child? tar在脚本中返回1,但不是交互式的 - tar returns 1 in script, but not interactively
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM