从Java通过SSH执行Bash命令时出现问题

时间:2014-06-18 06:51:06

标签: java bash ssh

我在使用Java通过SSH在远程GNU / Linux系统上执行命令时遇到问题。在本地Bash中执行时,以下命令可以正常工作(当然用户和主机不同但行为没有改变)。

$ ssh user@host.example.com 'hostname'
host
$ ssh user@host.example.com 'hostname -f'
host.example.com
$ ssh user@host.example.com "hostname -f"
host.example.com

在没有参数的情况下执行比hostname更复杂的事情,我认为从Java中做同样的事情是失败的。

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;

public class SOPlayground {

    public static void main(String[] args) throws Exception {
        for (String argument : new String[]{"hostname", "'hostname'", "\"hostname\"",
            "'hostname -f'", "\"hostname -f\""}) {
            CommandLine commandLine = new CommandLine("ssh");
            commandLine.addArgument("user@host.example.com");
            commandLine.addArgument(argument);
            System.out.println(commandLine);

            final Executor executor = new DefaultExecutor();

            try (ByteArrayOutputStream os = new ByteArrayOutputStream();
                    ByteArrayOutputStream err = new ByteArrayOutputStream()) {
                executor.setStreamHandler(new PumpStreamHandler(os, err));
                int exitcode = executor.execute(commandLine);
                System.out.println("exitcode=" + exitcode);
                System.out.println(new String(os.toByteArray(), "UTF-8"));
                System.err.println(new String(err.toByteArray(), "UTF-8"));
            } catch (IOException ex) {
                System.err.println(ex.getMessage());
            }
        }
    }
}

输出结果为:

ssh user@host.example.com hostname
exitcode=0

host

ssh user@host.example.com 'hostname'
exitcode=0

host

ssh user@host.example.com "hostname"
exitcode=0
host


ssh user@host.example.com 'hostname -f'
Process exited with an error: 127 (Exit value: 127)
ssh user@host.example.com "hostname -f"
Process exited with an error: 127 (Exit value: 127)

正如您所看到的,通过SSH从SSH执行hostname -f失败,退出代码为127.我想知道什么 bash(本地或远程)无法找到什么命令。

我试图使用变体

addArgument(String argument, boolean handleQuoting)

但结果没有差异。

我如何从通过SSH工作的Java构建CommandLine

2 个答案:

答案 0 :(得分:2)

您可以将JSch与publickey身份验证一起使用。

如果您只想使用exec执行单个远程命令然后关闭连接,那么您有一个有效的示例:

public static void main(String[] args) {

        String user = "--";
        String host = "--";

        try
        {
            JSch jsch = new JSch();
            // key authentication
            jsch.addIdentity("id_rsa");
            // open a new session on port 22
            Session session = jsch.getSession(user, host, 22);
            session.setConfig("StrictHostKeyChecking", "no");

            session.connect();
            String command = "ls /";
            Channel channel = session.openChannel("exec");
            ((ChannelExec) channel).setCommand(command);

            channel.setInputStream(null);

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

            InputStream in = channel.getInputStream();

            channel.connect();
            StringBuilder sb = new StringBuilder();
            byte[] tmp = new byte[1024];
            while (true)
            {
                while (in.available() > 0)
                {
                    int i = in.read(tmp, 0, 1024);
                    if (i < 0)
                        break;
                    sb.append(new String(tmp, 0, i));
                }
                if (channel.isClosed())
                {
                    if (in.available() > 0)
                        continue;
                    System.out.println("exit-status: "
                            + channel.getExitStatus());
                    break;
                }
                try
                {
                    Thread.sleep(500);
                }
                catch (Exception ee)
                {
                }
            }
            //disconnecting and closing
            channel.disconnect();

            session.disconnect();
            System.out.println("Output: ");
            System.out.println(sb.toString());
        }
        catch (Exception e)
        {
             //something should be done here
            e.printStackTrace();

        }
    }

输出:

exit-status: 0
Output: 
1
bin
boot
cgroup
dev
etc
home
lib
lib64
lost+found
....

希望有所帮助

注意:id_rsa是密钥文件的路径

答案 1 :(得分:0)

感谢有关Jsch的答案。我尝试了一种不同的方法,将命令写入临时文件,然后在本地执行。

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;
import org.apache.commons.io.IOUtils;

public class SOPlayground {

    public static void main(String[] args) throws Exception {
        final String command = "ssh user@host 'hostname -f'";
        int exitCode = executeCommand(command);
    }

    private static int executeCommand(final String command) {
        int exitcode = -1;
        File temp = null;
        try {
            temp = File.createTempFile("foo", ".tmp");
            try (OutputStream os = new FileOutputStream(temp);) {
                IOUtils.write(command, os);
            } finally {
                // os is closed
            }

            CommandLine commandLine = new CommandLine("bash");
            commandLine.addArgument(temp.getAbsolutePath());

            final Executor executor = new DefaultExecutor();
            try (ByteArrayOutputStream os = new ByteArrayOutputStream();
                    ByteArrayOutputStream err = new ByteArrayOutputStream()) {
                executor.setStreamHandler(new PumpStreamHandler(os, err));
                exitcode = executor.execute(commandLine);
                System.out.println("exitcode=" + exitcode);
                System.out.println(new String(os.toByteArray(), "UTF-8"));
                System.err.println(new String(err.toByteArray(), "UTF-8"));
            } finally {
                // os and err are closed
            }
        } catch (IOException ex) {
            System.err.println(ex.getMessage);
        } finally {
            if (temp != null) {
                temp.delete();
            }
        }
        return exitcode;
    }
}