我正在使用一种特殊的方式,可以让我访问谷歌。现在页面中的情况非常不同......所以我可以正常编辑问题并评论答案。我删除了更多早期的讨论并直奔主题。
当为脚本找到关键字以执行下一个命令时,我想停止拖尾并返回 0。如果一分钟后没有找到关键字,则整个脚本将停止并返回错误代码。我用set -euxo pipefail
的是必要的。
timeout 1m tail -Fn 0 --pid=$(ps -ef | grep "sed /$keywords" | grep -v grep | awk '{print $2}') $log_file | sed "/$keywords/q"
当我测试时,我之前使用的上面的命令很好。但在 Jenkins 中,有时它会在找到关键字时返回“构建步骤‘执行 shell’标记构建为失败”。
当我手动重新启动程序时,我找到了原因。它返回代码 141。所以我查找了代码,发现它与http://www.pixelbeat.org/programming/sigpipe_handling.htmltail -f
中的管道|
相关。
为了我的目的,我修改了其他问题中的命令。除了“tail -Fn 0 balabala.log”仍然留在后台,它似乎很好,几分钟后消失了。但它离目标最近。
{ sed /"$keywords"/q; kill -13 $!; } < <(exec timeout 1m tail -Fn 0 $log_file)
这超出了我的理解……我查了用法,但仍然不确定。
- 我改为
kill -s PIPE "$!"
缩短kill -13 $!
脚本。- 我仍然对
{ } < <()
. 它们对我来说就像陌生的词......- 可以
exec
删除吗?好像和不写不一样。tail
后台有什么问题?如果我同时启动多个程序会不会很危险?
这是詹金斯的日志:
......
+ keywords='cloud-service-notice has been started successfully'
+ log_file=/data/jars/logs/info.cloud-service-notice.log
+ cd /data/jars/cloud-service-notice
+ nohup java -jar /data/jars/cloud-service-notice/cloud-service-notice.jar --spring.profiles.active=test
+ sed '/cloud-service-notice has been started successfully/q'
++ exec timeout 1m tail -Fn 0 /data/jars/logs/info.cloud-service-notice.log
2019-07-02 10:31:12,544 [main] INFO o.s.c.a.AnnotationConfigApplicationContext.prepareRefresh[588] - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@54a097cc: startup date [Tue Jul 02 10:31:12 CST 2019]; root of context hierarchy
......
2019-07-02 10:31:33,860 [main] INFO c.enneagon.service.notice.NoticeApp.main[24] - cloud-service-notice has been started successfully
+ kill -13 20021
+ ssh web01 'cd /data/releases/cloud-service-notice/20190702-104108/.. &&' 'ls -1 | sort -r | awk '\''FNR > 20 {printf("rm -rf %s\n", $0);}'\'' | bash'
Finished: SUCCESS
进程 20021 是timeout 1m tail -Fn 0 balabala.log
. 脚本完成后,进程 20023tail -Fn 0 balabala.log
仍然存在,几分钟后消失。
[root@web01 scripts]# ps -ef | grep notice
root 20020 1 27 10:41 ? 00:01:07 java -jar /data/jars/cloud-service-notice/cloud-service-notice.jar --spring.profiles.active=test
root 20023 1 0 10:41 ? 00:00:00 tail -Fn 0 /data/jars/logs/info.cloud-service-notice.log
root 20461 18966 0 10:45 pts/1 00:00:00 grep --color=auto notice
我会用这个命令回答我的问题,但我不确定。我在本地机器上进行了测试,并将其放在我们的生产环境中进行进一步测试。
经过多次测试,我终于将命令更改为:
{ sed /"$keywords"/q; kill $!; } < <(exec timeout 1m tail -Fn 0 $log_file)
-13
只是被删除。tail -Fn 0 balabala.log
这就是留下来的原因。我可以大致回答上面的四个问题:
kill -15
更好,因为我timeout
在这个命令中添加了。$!
是 的 pidtimeout
。tail -Fn 0 balabala.log
这里是一个子进程,使用默认数字 15 可以杀死它。- 它只是一个进程替换和多个进程使用
{...}
. 我什至可以忽略kill
,因为 1 分钟后timeout
会在后台杀死自己。所以这个没有kill
的命令仍然是可以接受的:sed /"$keywords"/q < <(exec timeout 1m tail -Fn 0 $log_file)
. 在这种情况下,它将始终返回 0。- 最好别。执行命令时会显示两个上层进程。不过完成后就没事了。
- 原因在上面的“1”中。
timeout
被杀,tail
却被留下。
您可以执行以下操作:
除非您启用了该
pipefail
选项,否则它将以退出状态退出,其退出状态kill
应为 0,除非tail
它自己退出(这在实践中不应该发生)。使用
pipefail
,这将退出退出状态为tail
被 SIGPIPE 杀死,(大多数 shell 用值 141 表示)。您始终可以附加 a|| true
以强制成功退出状态。也可以看看:
我认为这里发生的事情是一种竞争条件。
tail --pid=...
取决于tail
检查并意识到被跟踪的 PID 在尝试写入因sed
终止而中断的管道之前已经死亡。手册只说“在该进程终止后不久,tail 也将终止”,因此检查可能不频繁。无论如何,如果
tail
确实被 SIGPIPE 杀死,这意味着(通常)sed
已经得到了你想要的线路并且已经退出,所以这对你来说也应该是一个成功的条件。而且我建议将其视为完全一样,使用if
块来防止set -e
,并明确检查退出状态:旁白:你说脚本使用
#/bin/bash
. 那应该#!
带有感叹号,而不仅仅是#
。否则,脚本将与 sh 一起运行,这可能就是您尝试使用进程替换(<(...)
是进程,而不是命令,替换)是语法错误的原因。仔细检查。我知道你说过你想要一个单线,但就可维护性而言,复杂的单线并不总是最好的解决方案。如果这意味着更容易记录您正在做的事情,请使用多行。
我找到了一个命令。细节在问题中。
#!/bin/bash
并且set -euxo pipefail
如果在脚本中是必需的,它必须由bash
, not执行sh
,尽管bash
在 CentOS 中链接到。