当使用 JSch 通过 Java 执行时,某些 Unix 命令失败并显示“...未找到”

Certain Unix commands fail with "... not found", when executed through Java using JSch

我有一段代码连接到 Unix 服务器并执行命令。

我一直在尝试使用简单的命令,它们运行良好。

我能够登录并获得命令的输出。

我需要 运行 通过 Java 的从头算图。

我正在为此使用 air sandbox run graph 命令。

它 运行 很好,当我使用 SSH 客户端和 运行 命令登录时。我可以 运行 图表。但是,当我尝试通过 Java 运行 命令时,它给了我一个 "air not found" 错误。

JSch 支持的 Unix 命令有什么限制吗?

知道为什么我无法通过我的 Java 代码 运行 命令吗?

代码如下:

public static void connect(){
    try{
      JSch jsch=new JSch();  

      String host="*****";
      String user="*****";
      String config =
                "Host foo\n"+
                "  User "+user+"\n"+
                "  Hostname "+host+"\n";

      ConfigRepository configRepository =
            com.jcraft.jsch.OpenSSHConfig.parse(config);

      jsch.setConfigRepository(configRepository);
      Session session=jsch.getSession("foo");

      String passwd ="*****";
      session.setPassword(passwd);
      UserInfo ui = new MyUserInfo(){

          public boolean promptYesNo(String message){

            int foo = 0;
            return foo==0;
          }
      };
      session.setUserInfo(ui);
      session.connect();

      String command="air sandbox run <graph-path>";

      Channel channel=session.openChannel("exec");
      ((ChannelExec)channel).setCommand(command);

      channel.setInputStream(null);

      ((ChannelExec)channel).setErrStream(System.err);

      InputStream in=channel.getInputStream();

      channel.connect();

      byte[] tmp=new byte[1024];
      while(true){
        while(in.available()>0){
          int i=in.read(tmp, 0, 1024);
          if(i<0)break;
          page_message=new String(tmp, 0, i);
          System.out.print(page_message);
        }
        if(channel.isClosed()){
          if(in.available()>0) continue; 
          System.out.println("exit-status: "+channel.getExitStatus());
          break;
        }
        try{Thread.sleep(1000);}catch(Exception ee){}
      }

      channel.disconnect();
      session.disconnect();
    }
    catch(Exception e){
      System.out.println(e);
    }
}

public static void main(String arg[]){
    connect();
}

public String return_message(){
      String ret_message=page_message;
      return ret_message;
}

public static abstract class MyUserInfo
    implements UserInfo, UIKeyboardInteractive{
    public String getPassword(){ return null; }
    public boolean promptYesNo(String str){ return false; }
    public String getPassphrase(){ return null; }
    public boolean promptPassphrase(String message){ return false; }
    public boolean promptPassword(String message){ return false; }
    public void showMessage(String message){ }
    public String[] promptKeyboardInteractive(String destination,
                        String name,
                        String instruction,
                        String[] prompt,
                        boolean[] echo){
        return null;
    }
}

JSch 中的 "exec" 频道(理所当然地)不为会话分配伪终端 (PTY)。因此,(可能)获取了一组不同的启动脚本(特别是对于非交互式会话,.bash_profile 未获取)。 And/or 根据 TERM 环境变量的 absence/presence,采用脚本中的不同分支。因此环境可能与您使用 SSH 客户端的交互式会话不同。

因此,在您的情况下,PATH 的设置可能不同;因此无法找到 air 可执行文件。

要验证这是根本原因,请在您的 SSH 客户端中禁用伪终端分配。例如在 PuTTY 中,它是 Connection > SSH > TTY > Don't allocate a pseudo terminal。然后,转到 Connection > SSH > Remote command 并输入 air ... 命令。选中 Session > Close window on exit > Never 并打开会话。你应该得到相同的 "air not found" 错误。


解决此问题的方法,按优先顺序排列:

  1. 修复命令不依赖于特定环境。在命令中使用 air 的完整路径。例如:

    /bin/air sandbox run <graph-path>
    

    如果您不知道完整路径,在常见的 *nix 系统上,您可以在交互式 SSH 会话中使用 which air 命令。

  2. 修复您的启动脚本,为交互式和非交互式会话设置相同的PATH

  3. 通过登录 shell 显式尝试 运行 脚本(将 --login 开关与通用 *nix shells 一起使用):

    bash --login -c "air sandbox run sandbox run <graph-path>"
    
  4. 如果命令本身依赖于特定的环境设置并且您无法修复启动脚本,则可以在命令本身中更改环境。那取决于远程系统的语法 and/or shell。在常见的 *nix 系统中,这有效:

    String command="PATH=\"$PATH;/path/to/air\" && air sandbox run <graph-path>";
    
  5. 另一种(不推荐)方法是使用.setPty方法强制为"exec"频道分配伪终端:

    Channel channel = session.openChannel("exec");
    ((ChannelExec)channel).setPty(true);
    

    使用伪终端自动执行命令会给您带来严重的副作用。例如参见 [​​=24=]


对于类似的问题,请参阅

你可以尝试找出 "air" 与

的位置
whereis air

然后使用这个结果。

类似

/usr/bin/air sandbox run graph

您可以使用 ~/.ssh/environment 文件来设置您的 AB_HOME 和 PATH 变量。