在Jenkins中,您可以使用sh
步骤来运行Unix shell脚本。
我在做实验,发现stdout不是tty,至少在Docker映像上是这样。
Jenkins使用什么来捕获通过sh
步骤运行的程序的stdout和stderr?在Jenkins节点上运行sh
步骤与在Docker容器上运行相同吗?
我请求得到我自己的启迪,并对这些知识进行一些可能的实际应用。
重现我的实验
如果你已经知道答案,你不需要阅读这些细节来复制。我在这里添加这个只是为了再现性。
我有以下Jenkins/Groovy代码:
docker.image('gcc').inside {
sh '''
gcc -O2 -Wall -Wextra -Wunused -Wpedantic
-Werror write_to_tty.c -o write_to_tty
./write_to_tty
'''
}
上面sh
步骤代码的Jenkins日志片段是
+ gcc -O2 -Wall -Wextra -Wunused -Wpedantic -Werror write_to_tty.c -o write_to_tty
+ ./write_to_tty
stdout is not a tty.
这编译并运行以下C代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int stdout_fd = fileno(stdout);
if (!isatty(stdout_fd)) {
fprintf(stderr, "stdout is not a tty.n");
exit(1);
}
char* stdout_tty_name = ttyname(stdout_fd);
if (stdout_tty_name == NULL) {
fprintf(stderr, "Failed to get tty name of stdout.n");
exit(1);
}
FILE* tty = fopen(stdout_tty_name, "w");
if (tty == NULL) {
fprintf(stderr, "Failed to open tty %s.n", stdout_tty_name);
exit(1);
}
fprintf(tty, "Written directly to tty.n");
fclose(tty);
printf("Written to stdout.n");
fprintf(stderr, "Written to stderr.n");
exit(0);
}
我简要查看了这里的源代码,似乎stdout
被写入到一个文件中,然后从中读取,因此它不是tty。此外,stderror
(如果有的话(将被写入日志。
这是Javadoc。
/***运行持久任务,例如shell脚本,通常在代理人。*
在这种情况下,"耐用"意味着詹金斯尝试保持外部进程运行*即使Jenkins控制器或代理JVM重新启动。*工艺标准输出指向工作区附近的文件,而不是保存文件句柄打开。*每当两者之间的远程处理连接可以重新建立,*詹金斯再次寻找此后发送的任何输出上次检查的时间。*当进程退出时,状态代码也写入到文件中,并最终导致步骤通过或失败。*
任务也可以在内置节点上运行不同之处仅在于不存在网络故障的可能性。*/
我找到了sh
步骤的源代码,该步骤似乎是使用BourneShellScript
类实现的。
当不捕获stdout时,会生成如下命令:
cmdString = String.format("{ while [ -d '%s' -a \! -f '%s' ]; do touch '%s'; sleep 3; done } & jsc=%s; %s=$jsc %s '%s' > '%s' 2>&1; echo $? > '%s.tmp'; mv '%s.tmp' '%s'; wait",
controlDir,
resultFile,
logFile,
cookieValue,
cookieVariable,
interpreter,
scriptPath,
logFile,
resultFile, resultFile, resultFile);
如果我正确地将格式说明符与变量匹配,那么%s '%s' > '%s' 2>&1
部分大致对应于
${interpreter} ${scriptPath} > ${logFile} 2>&1
因此,脚本的stdout和stderr似乎被写入了一个文件。
当捕获输出时,它略有不同,而是
${interpreter} ${scriptPath} > ${c.getOutputFile(ws)} 2> ${logFile}
在这种情况下,stdout和stderr仍然被写入文件,只是不写入同一个文件。
附加
对于任何对我如何找到源代码感兴趣的人:
- 首先,我使用了
sh
步骤文档的书签,它可以通过搜索引擎找到 - 我滚动到顶部,点击";在插件网站上查看此插件";链接
- 在那个页面上,我点击了GitHub链接
- 在GitHub repo上,我通过从
src
目录向下搜索导航到ShellStep.java
- 我看到这个类是使用
BourneShellScript
类实现的,根据这个类的导入,我知道它是持久任务插件的一部分 - 我搜索了持久任务插件,并遵循了相同的"在插件网站上查看此插件";链接,然后是GitHub链接,以查看持久任务插件的GitHub回购
- 接下来,我使用";转到文件";按钮,并搜索要跳转到其
.java
文件的类名 - 最后,我检查了这份文件,找到了我感兴趣的内容