某些 Unix 命令在使用 JSch 通过 Java 执行时失败并显示“... not found”

Posted

技术标签:

【中文标题】某些 Unix 命令在使用 JSch 通过 Java 执行时失败并显示“... not found”【英文标题】:Certain Unix commands fail with "... not found", when executed through Java using JSch 【发布时间】: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;
        
        tryThread.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;
    

【问题讨论】:

【参考方案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”错误。


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

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

    /bin/air sandbox run <graph-path>
    

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

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

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

    bash --login -c "air sandbox run sandbox run <graph-path>"
    

    如果命令本身依赖于特定的环境设置并且您无法修复启动脚本,您可以在命令本身中更改环境。其语法取决于远程系统和/或 shell。在常见的 *nix 系统中,这是有效的:

    String command="PATH=\"$PATH;/path/to/air\" && air sandbox run <graph-path>";
    

    另一种(不推荐)方法是使用.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?


关于类似问题,请参阅

Certain Unix commands fail with "... not found", when executed through Java using JSch even with setPty enabled Commands executed using JSch behaves differently than in SSH terminal (bypasses confirm prompt message of "yes/"no") JSch: Is there a way to expose user environment variables to "exec" channel? Command (.4gl) executed with SSH.NET SshClient.RunCommand fails with "No such file or directory"

【讨论】:

啊。谢谢,成功了。【参考方案2】:

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

【讨论】:

【参考方案3】:

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

whereis air

然后使用这个结果。

类似

/usr/bin/air sandbox run graph

【讨论】:

以上是关于某些 Unix 命令在使用 JSch 通过 Java 执行时失败并显示“... not found”的主要内容,如果未能解决你的问题,请参考以下文章

使用 JSch 的多个命令

Java中通过jsch来连接远程服务器执行linux命令

使用 JSch 通过 SSH 运行命令

通过 JSch shell 执行多个命令

使用 JSch 为通过 SSH 执行的命令提供输入/子命令

使用java中的jsch在linux中通过sudo命令执行shell脚本以启动服务