首页 > 解决方案 > 通过 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());

标签: javalinuxssh

解决方案


上面代码的亮点在这 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 0or 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 0or exit 1,你可以写

echo 0 > ./export/home/therock/test/result.txt
exit 0

./export/home/therock/test/result.txt所以您知道如果文件不存在,bash 脚本仍在运行。如果文件存在,则意味着 bash 脚本已完成,您可以阅读其内容以查看其结果。

其他方法(更推荐)是编写一个锁定文件,该文件在 bash 脚本进程启动时创建,并在退出时删除。这样,它还可以确保您的 bash 脚本一次只执行一次。


推荐阅读