【问题标题】:Certain Unix commands fail with "... not found", when executed through Java using JSch某些 Unix 命令在使用 JSch 通过 Java 执行时失败并显示“... not found”
【发布时间】:2017-02-22 15:04:54
【问题描述】:

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

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

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

我需要通过 Java 运行 Ab-initio 图。

我正在为此使用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;
    }
}

【问题讨论】:

    标签: java shell unix ssh jsch


    【解决方案1】:

    JSch 中的“exec”通道(正确地)没有为会话分配伪终端 (PTY)。因此,(可能)获取了一组不同的启动脚本(特别是对于非交互式会话,.bash_profile 未获取)。和/或脚本中的不同分支基于TERM 环境变量的缺失/存在而被采用。因此,环境可能与您与 SSH 客户端一起使用的交互式会话不同。

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

    要验证这是根本原因,请在 SSH 客户端中禁用伪终端分配。例如在 PuTTY 中,它是 Connection > SSH > TTY > Don't allocate a pseudo terminal。然后,转到 Connection > SSH > Remote command 并输入您的 air ... 命令。检查会话>退出时关闭窗口>从不并打开会话。你应该得到同样的“air not found”错误。


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

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

      /bin/air sandbox run <graph-path>
      

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

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

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

      bash --login -c "air sandbox run sandbox run <graph-path>"
      
    4. 如果命令本身依赖于特定的环境设置并且您无法修复启动脚本,您可以在命令本身中更改环境。其语法取决于远程系统和/或 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);
      

      使用伪终端自动执行命令会给您带来令人讨厌的副作用。参见例如Is there a simple way to get rid of junk values that come when you SSH using Python's Paramiko library and fetch output from CLI of a remote machine?


    关于类似问题,请参阅

    【讨论】:

    • 啊。谢谢,成功了。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      你可以试着找出“空气”在哪里

      whereis air
      

      然后使用这个结果。

      类似

      /usr/bin/air sandbox run graph
      

      【讨论】:

        猜你喜欢
        • 2021-08-07
        • 2017-11-07
        相关资源
        最近更新 更多