java - 通过 SSH 命令执行远程脚本:如何知道它是否正确完成
问题描述
我需要通过 SSH 和 Java 执行脚本 Linux,该脚本位于远程 Linux 机器上。在运行时,我从数据库中检索脚本的名称、主机和凭据,并尝试通过 ssh 启动脚本。这没关系,因为我得到了执行 PID 作为回报!问题是我总是得到一个执行的PID,即使脚本是错误的、它不存在或者它没有执行权。
有没有办法知道执行是否正确或命令是否错误?我需要知道在同一个 ssh 启动命令中是否有可能。先感谢您
我们使用的代码如下:
JSch jsch = new JSch();
String post_process_host="123.123.123.123";
Integer post_process_port=22;
String post_process_user="therock";
String post_process_pwd="XxXdfG$L";
//String command="testpp.sh";
String command="./export/home/therock/test/hello.bsh";
command=command + " > /dev/null 2>&1 & \n echo $!";
Session session = jsch.getSession(post_process_user, post_process_host, post_process_port.intValue());
UserInfo ui = new SUserInfo(post_process_pwd, null);
session.setUserInfo(ui);
session.setPassword(post_process_pwd);
session.connect();
int timeout=10; //timeout di 5 minuti
StringBuffer text = new StringBuffer();
int exitCode = -1;
try {
logger.info("Execution command: " + command);
Channel channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);
channel.setInputStream(null);
((ChannelExec) channel).setErrStream(System.err);
InputStream in = channel.getInputStream();
InputStream err = ((ChannelExec) channel).getErrStream();
channel.connect(timeout);
byte[] tmp = new byte[1024];
while (true) {
timeout--;
while (in.available() > 0) {
int i = in.read(tmp, 0, 1024);
if (i < 0)
break;
String line = new String(tmp, 0, i, StandardCharsets.UTF_8);
text.append(line);
if (text.toString().contains("\n")) break;
}
while (err.available() > 0) {
int i = err.read(tmp, 0, 1024);
if (i < 0)
break;
String line = new String(tmp, 0, i, StandardCharsets.UTF_8);
text.append(line);
}
if (channel.isClosed()) {
if (in.available() > 0)
continue;
exitCode = channel.getExitStatus();
logger.info("Eseguito comando codice di ritorno: " + exitCode);
logger.debug("output comando: "+text.toString());
if (exitCode!=0) throw new Exception("Execution Command "+command+" error with Return Code="+exitCode);
break;
}
try {
Thread.sleep(1000);
} catch (Exception ee) {
throw ee;
}
}
channel.disconnect();
} catch (Exception e) {
throw e;
}
logger.debug("output command: "+text.toString());
解决方案
上面代码的亮点在这 2 行:
String command="./export/home/therock/test/hello.bsh";
command=command + " > /dev/null 2>&1 & \n echo $!";
这使得command
变量具有这样的值:
./export/home/therock/test/hello.bsh > /dev/null 2>&1 & \n echo $!
要了解此命令的作用,您可能需要研究bash
,但这是我的快速解释:
- 在名为的文件中执行一个命令
./export/home/therock/test/hello.bsh
(从它的扩展名来看,我假设它是一个 shell 脚本) > /dev/null
= 然后将其重定向STDOUT
到/dev/null
(又名:丢弃输出)2>&1
= 然后将其重定向STDERR
到任何地方STDOUT
(如上所述,它去了 /dev/null 或丢弃)&
= 在后台执行\n echo $!
= 然后回显(在 STDOUT 中打印)生成的后台进程的 PID(进程 ID)
现在我不知道你的./export/home/therock/test/hello.bsh
文件的内容,但是如果它写得很好,那么它应该返回一个退出代码,表明它是否运行成功(例如:exit 0
用于成功运行,exit 1
用于失败运行,exit 2
用于其他类型的错误)
之后,我们可以看到最后一条命令的退出代码echo $?
因此,您可能希望将代码重写为:
String command="./export/home/therock/test/hello.bsh > /dev/null 2>&1 ; \n echo $?";
解释:
- 在名为的文件中执行一个命令
./export/home/therock/test/hello.bsh
(从它的扩展名来看,我假设它是一个 shell 脚本) > /dev/null
= 然后将其重定向STDOUT
到/dev/null
(又名:丢弃输出)2>&1
= 然后将其重定向STDERR
到任何地方STDOUT
(如上所述,它去了 /dev/null 或丢弃);
= 执行完这条命令后,执行下一条命令\n echo $?
= 然后回显(在 STDOUT 中打印)最后一个命令的退出代码
请注意,在此更改之后,您将不再拥有进程的 PID,而是拥有退出代码。而这个改变也会让你的 java 程序等待 bash 脚本完成。
所以如果你的 bash 脚本运行了 5 分钟,并且你在 08:00:00 开始执行你的 java 程序,
- 以前,您的 java 程序应该在大约 08:00:01 完成(非常快,因为它不等待 bash 脚本完成)
- 更改后,您的 java 程序将在大约 08:05:01 完成(因为它在退出之前等待 bash 脚本完成)
编辑:
OP说
业务需求是在后台在远程机器上启动一个脚本,所以我等不及脚本的完整执行。我个人的解决方案是在 SSH 启动时获取 PID,然后再次查询远程系统
您始终可以将退出代码保存到文件中,然后稍后读取该文件,例如:
在./export/home/therock/test/hello.bsh
, 而不是写exit 0
or exit 1
, 你可以写
echo 0 >> ./export/home/therock/test/result.txt
exit 0
稍后当你想检查结果时,你只需要阅读最后一行./export/home/therock/test/result.txt
(例如tail -n1 ./export/home/therock/test/result.txt
:)
编辑2:
OP 说他/她需要知道该过程是否已经完成。
简单的方法是简单地创建一个锁定文件(或反向锁定文件)
在开头./export/home/therock/test/hello.bsh
添加:
rm -f ./export/home/therock/test/result.txt
然后,不用写exit 0
or exit 1
,你可以写
echo 0 > ./export/home/therock/test/result.txt
exit 0
./export/home/therock/test/result.txt
所以您知道如果文件不存在,bash 脚本仍在运行。如果文件存在,则意味着 bash 脚本已完成,您可以阅读其内容以查看其结果。
其他方法(更推荐)是编写一个锁定文件,该文件在 bash 脚本进程启动时创建,并在退出时删除。这样,它还可以确保您的 bash 脚本一次只执行一次。
推荐阅读
- python - Django后台任务自动发现不起作用
- javascript - Async/Await vs new Promise(resolve,reject)
- reactjs - ReactJS:仅在使用组件时才加载 .less
- dart - showGeneralDialog 未按预期工作
- c# - 如何通过搜索数据库在 dataGridView 中添加新行
- r - 在 R 中仅使用一些属性进行聚类
- amazon-web-services - 在 Lambda 中使用 KCL 1.*:凭证
- angular - rxjs concatMap 似乎没有被调用
- android - shrinkResources 不工作动态功能模块 android
- javascript - 使用 Jquery 进行注释