- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我有许多执行许多类似任务的 bash 脚本,它们使用一些外部二进制程序。问题是二进制程序通常不会像它们应该的那样退出终止。由于我的脚本运行了数千次,因此这些进程的大量闲置/几乎死亡的实例很快就会累积起来。我无法修复这些程序,因此我需要确保我的 bash 脚本正在终止它们。
SE 中已经有一些主题处理终止 bash 脚本进程的任务。我已经应用并测试了那里写的东西,并且在某种程度上它是有效的。但它对我的情况来说效果不够好,我不明白为什么,因此我提出了一个新问题。
我的脚本有一个层次结构,这里以简化的方式显示:脚本 A 调用脚本 B,脚本 B 并行调用脚本 C 的多个实例以使用所有 CPU。例如。脚本 B 并行运行脚本 C 的 5 个实例,当脚本 C 的一个实例完成时,它会启动一个新实例,总共运行数千次脚本 C。脚本 C 调用几个外部二进制文件/命令,这些二进制文件/命令不能很好地终止。它们在后台并行并相互通信。
但是,我的脚本 C 能够检测到外部命令何时完成它们的工作,即使它们没有终止,然后我的 bash 脚本退出。
为了在 bash 脚本完成期间终止所有外部程序,我添加了一个退出陷阱:
# Exit cleanup
cleanup_exit() {
# Running the termination in an own process group to prevent it from preliminary termination. Since it will run in the background it will not cause any delays
setsid nohup bash -c "
touch /tmp/trace_1 # To see if this code was really executed to this point
# Trapping signals to prevent that this function is terminated preliminary
trap '' SIGINT SIGQUIT SIGTERM SIGHUP ERR
touch /tmp/trace_2 # To see if this code was really executed to this point
# Terminating the main processes
kill ${pids[@]} 1>/dev/null 2>&1 || true
touch /tmp/trace_3
sleep 5
touch /tmp/trace_4
kill -9 ${pids[@]} 1>/dev/null 2>&1 || true
touch /tmp/trace_5
# Terminating the child processes of the main processes
echo "Terminating the child processes"
pkill -P ${pids[@]} 1>/dev/null 2>&1 || true
touch /tmp/trace_6
sleep 1
pkill -9 -P ${pids[@]} 1>/dev/null 2>&1 || true
touch /tmp/trace_7
# Terminating everything else which is still running and which was started by this script
pkill -P $$ || true
touch /tmp/trace_8
sleep 1
pkill -9 -P $$ || true
touch /tmp/trace_9
"
}
trap "cleanup_exit" SIGINT SIGQUIT SIGTERM EXIT
现在,如果我只并行运行很少的脚本 C 实例,这似乎可行。如果我将数字增加到更多,例如10(工作站功能强大,应该可以并行处理几十个脚本C和外部程序的并行实例),然后就不行了,外部程序的数百个实例正在快速积累。
但是我不明白为什么。例如,累积的其中一个进程的 PID 是 32048。在日志中我可以看到退出陷阱的执行:
+ echo ' * Snapshot 190 completed after 3 seconds.'
* Snapshot 190 completed after 3 seconds.
+ break
+ cleanup_exit
+ echo
+ echo ' * Cleaning up...'
* Cleaning up...
+ setsid nohup bash -c '
touch /tmp/trace_1 # To see if this code was really executed to this point
# Trapping signals to prevent that this function is terminated preliminary
trap '\'''\'' SIGINT SIGQUIT SIGTERM SIGHUP ERR
touch /tmp/trace_2 # To see if this code was really executed to this point
# Terminating the main processes
kill 31678' '32048 1>/dev/null 2>&1 || true
touch /tmp/trace_3
sleep 5
touch /tmp/trace_4
kill -9 31678' '32048 1>/dev/null 2>&1 || true
touch /tmp/trace_5
# Terminating the child processes of the main processes
pkill -P 31678' '32048 1>/dev/null 2>&1 || true
touch /tmp/trace_6
sleep 1
pkill -9 -P 31678' '32048 1>/dev/null 2>&1 || true
touch /tmp/trace_7
# Terminating everything else which is still running and which was started by this script
pkill -P 31623 || true
touch /tmp/trace_8
sleep 1
pkill -9 -P 31623 || true
touch /tmp/trace_9
'
很明显,exit trap中使用了这个进程的PID,但是进程并没有退出。为了测试,我在此进程上再次手动运行 kill 命令,然后它确实退出了。
最有趣的是,只出现了直到编号 5 的跟踪文件。没有超过 5,但为什么呢?
更新:我刚刚发现,即使我只并行运行脚本 C 的一个实例,即顺序运行,它也只能在一段时间内运行良好。突然在某个时间点进程不再终止,而是开始永远徘徊并积累。机器不应因并行的一个过程而重载。在我的日志文件中,退出陷阱仍然像以前一样正确调用,没有区别。内存也是免费的,CPU 也是部分免费的。
最佳答案
对任何 shell 脚本进行健全性检查的一个很好的方法是在其上运行 ShellCheck:
Line 9:
kill ${pids[@]} 1>/dev/null 2>&1 || true
^-- SC2145: Argument mixes string and array. Use * or separate argument.
确实,您的 xtrace 在这一行做了一些奇怪的事情:
kill 31678' '32048 1>/dev/null 2>&1 || true
^^^--- What is this?
这里的问题是你的 ${pids[@]}
扩展成多个单词,而 bash -c
只解释第一个单词。这是一个简化的示例:
pids=(2 3 4)
bash -c "echo killing ${pids[@]}"
这最终写成killing 2
,没有提到3或4。相当于运行
bash -c "echo killing 2" "3" "4"
其他 pid 只是成为位置参数 $0
和 $1
而不是已执行命令的一部分。
相反,喜欢 ShellCheck suggested ,您希望 *
将所有 pids 与空格连接起来并将它们作为单个参数插入:
pids=(2 3 4)
bash -c "echo killing ${pids[*]}"
打印killing 2 3 4
。
关于linux - 杀死由 Bash 脚本启动的所有进程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46513568/
我用 IntelliJ IDEA 2021.1 CE 在 流行!_OS 20.04 与 bash 5.0.17 . 问题造句:我将IntelliJ终端设置为/bin/bash通过 IntelliJ 设
给定如下命令: bash --shortcuts 我想显示一个快捷方式列表,就像在这个页面上一样: http://www.skorks.com/2009/09/bash-shortcuts-for-m
我有一个脚本可以操作数据、创建参数并将它们发送到第二个脚本。其中一个参数包含一个空格。 脚本1.sh: args=() args+=("A") args+=("1 2") args+=("B") .
我的脚本的“只运行一次”版本的一个非常简单的示例: ./myscript.sh var1 "var2 with spaces" var3 #!/bin/bash echo $1 #output: va
我想了解数字( double )在 bash 中是如何表示的,以及当我在 bash 中以十六进制格式打印数字时会发生什么。 根据 IEEE 754 标准,double 应由 64 位表示:52 位(1
我试图在 bash -c "..." 命令中获取 bash 脚本,但它不起作用。 如果我在 bash -c "..." 之外运行命令,它会起作用。 我需要使用 bash -c "..." 因为我想确保
如何检测我的 bash shell 中是否加载了 bash 补全包?从 bash-completion 的 2.1 版(包含在 Debian 8 中)开始,除了 BASH_COMPLETION_COM
我的 bash_profile 中有一个投影函数。现在我试图从 bash 脚本中调用这个函数,但是我得到了一个未找到的错误。如何使投影函数对 bash 脚本可见? 最佳答案 必须导出函数 export
我正在编写一个 bash 脚本,它接受许多命令行参数(可能包括空格)并通过登录 shell 将它们全部传递给程序 (/bin/some_program)。从 bash 脚本调用的登录 shell 将取
当我创建一个新的 bash 进程时,提示符默认为一个非常简单的提示符。我知道我可以编辑 .bashrc 等来更改它,但是有没有办法使用 bash 命令传递提示? 谢谢! 最佳答案 提示由 PS1、PS
好的,我希望这个问题有一定道理,但是 bash shell 和 bash 终端之间有什么区别?例子。当我第一次打开终端时,会提示我当前的目录和用户名。在终端窗口标题中显示 -bash- ,当我键入 e
我是 SBCL 的新手,我正在尝试从 bash 终端运行存储在文本文件中的 Lisp 脚本。 这是我在文件开头写的内容 http://www.sbcl.org/manual/#Running-from
我知道我们可以在 bash 中使用将十六进制转换为十进制 #!/bin/bash echo "Type a hex number" read hexNum echo $(( 16#$hexNum ))
我正在尝试在 bash 脚本中自动完成文件夹名称。如果我输入完整的文件夹名称,一切正常,但我不知道如何自动完成名称。有什么想法吗? repo() { cd ~/Desktop/_REPOS/$1 }
我想检查远程网站上的一些文件。 这里是bash命令生成计算文件md5的命令 [root]# head -n 3 zrcpathAll | awk '{print $3}' | xargs -I {}
是否有任何内置函数可以使用 bash shell 脚本从给定日期获取下周日(下周一、下周二等)?例如,2014 年 9 月 1 日之后的第一个星期日是什么时候?我预计 2014 年 9 月 7 日。
我一直在尝试根据表格重命名一些特定文件,但没有成功。它要么重命名所有文件,要么给出错误。 该目录包含数百个以长条形码命名的文件,我只想重命名包含模式 _1_ 的文件。 例子 barcode_1_bar
bash 中有没有办法用变量的内容替换文本文件中的占位符? 例如,我想发送一封电子邮件通知,如下所示: Dear Foo, Alert: blah blah blah blah blah blah
我有一个 bash 脚本,它在某些字符串上附加了一个重音字符,导致它失败,我找不到这些字符在哪里或如何进入那里。 这是一些示例输出: mv: cannot move â/tmp/myapp.zipâ
这个问题在这里已经有了答案: How do I place stdout on edit line? (1 个回答) Can a bash script prepopulate the prompt
我是一名优秀的程序员,十分优秀!