将 SCP 与 JSch 一起使用时如何转义双引号?

How can I escape doublequotes when using SCP with JSch?

在 Windows 系统上安装了 SSH,并与远程主机 (Linux) 交换密钥。我想 运行 windows 机器上的一个 JAR 到 Linux 机器上的 SCP 文件。 使用

在命令行上执行此操作
"C:/Program Files (X86)/ICW/bin/SCP.exe" -i .ssh/id_rsa <filename> theUser@xxx.xxx.xx.xx:/target/path/<filename>

效果很好(使用双引号)。

但是当我使用 JSch 从 JAR 中 运行 SCP 时,我得到一个 sun.nio.file.InvalidPathException: Illegal char <"> at index...

目的是我必须以编程方式从 FTP 主机收集一堆文件(我已经让这部分工作了)。这些文件必须转发到已经提到的远程主机。 FTP 主机和远程主机不能相互直接通信。因此,Windows 机器充当了一种代理。 JAR 从 FTP 服务器获取文件(目前将它们保存在地图中),我想使用 JSch SCP 将它们直接放入远程 Linux 主机上的特定文件夹中。

我该如何解决这个问题?如何解决这个 双引号问题?

    private void storeFiles() throws IOException {
        final JSch jsch = new JSch();
        final FileSystem fs = FileSystems.getDefault();
        final Path p = fs.getPath( getProperty("scp.key.file.private"));
        logger.info("Reading private key from " + getProperty("scp.key.file.private"));

        final Session session;
        try {
            session = jsch.getSession(getProperty("scp.user.name"), getProperty("scp.host.ip"),
                    Integer.parseInt(getProperty("scp.port")));
            jsch.addIdentity(p.toFile().getPath());
            final Properties config = new java.util.Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);
            session.connect();
            logger.info("Connected to " + getProperty("scp.host.ip") + "...");

            vaFiles.forEach((k, v) ->{
                // get I/O streams for remote scp
                try {
                    final MessageFormat mf = new MessageFormat(getProperty("scp.command"));
                    final Object[] commandArgs = new Object[]{getProperty("scp.key.file.private"),
                            k, getProperty("scp.user.name"),
                            getProperty("scp.host.ip"),
                            getProperty("scp.target.path"), k};
                    final String command = mf.format(commandArgs);
                    logger.info(command);
                    final Channel channel;
                    try {
                        channel = session.openChannel("exec");
                        channel.connect();
                        ((ChannelExec)channel).setCommand(command);
//                        logger.info("Writing " + Arrays.toString(v) + " to SCP channel...");
                        final OutputStream out = channel.getOutputStream();
                        out.write(command.getBytes());
                        out.flush();
                        out.write(v);
                        out.close();
                        channel.disconnect();
                    } catch (JSchException e) {
                        e.printStackTrace();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }); //end forEach
            session.disconnect();
        } catch (JSchException e) {
            e.printStackTrace();
        }
    }

其中 vaFiles 是 Map 并且 scp.command 来自属性文件并且是

scp.command = '\"C\:/Program\u0020Files\u0020(x86)/ICW/bin/scp.exe\"' -i {0} {1} {2}@{3}\:{4}/{5} 

(我试过把命令字符串用单引号、双引号、不带引号、不带\u0020、...)

正在使用 MessageFormat 填充

{0} = '\"C:/Program\u0020Files\u0020(x86)/ICW/bin/scp.exe\"'

{1} = 文件名

{2} = scp 用户名

{3} = scp hist IP 地址

{4} = 远程主机上的目标路径

{5} = 文件名(参见{1})

SFTP目前没有选项,只有SCP安装在Windows机器和远程Linux主机上。 如果我在这样做时完全误解了,我将不胜感激任何知识转移以学习如何以正确的方式做到这一点。 :-)

您显然是在尝试启动从 本地 Windows 机器到 远程 Linux[=36] 的 SCP 文件传输=]机.

但是您的代码所做的是,它通过 ssh 连接到 远程 Linux 机器并尝试使用 运行 Windows 样式的命令C:\Program Files (x86)\ICW\bin\scp.exe 那里。那不行,不管你用什么报价。

  1. 不要使用 SCP,这是一个古老的协议。使用 SFTP。所有 Linux 台机器都支持 SFTP。 JSch 也有对 SFTP 的本地支持。您不需要任何本地 OpenSSH 安装。

  2. 即使您决定坚持使用 SCP,将 JSch SSH/SFTP 库与本地独立 SCP 客户端结合使用也没有任何意义。

好的,Martin,谢谢你指引我正确的方向。可能我会检查并尝试使用 SFTP 来实现它。 目前你帮助我至少实现了一个使用 SCP 的工作版本(即使它是一个古老的协议):

private void storeFilesSCP() throws IOException {
    final JSch jsch = new JSch();
    final FileSystem fs = FileSystems.getDefault();
    final Path p = fs.getPath( getProperty("scp.key.file.private"));
    logger.info("Reading private key from " + getProperty("scp.key.file.private"));

    final Session session;
    try {
        session = jsch.getSession(getProperty("scp.user.name"), getProperty("scp.host.ip"),
                Integer.parseInt(getProperty("scp.port")));
        jsch.addIdentity(p.toFile().getPath());
        final Properties config = new java.util.Properties();
        config.put("StrictHostKeyChecking", "no");
        session.setConfig(config);
        final UserInfo userInfo = new UserInfo();
        session.setUserInfo(userInfo);
        session.connect();
        logger.info("Connected to " + getProperty("scp.host.ip") + "...");

        vaFiles.forEach((k, v) ->{
            // get I/O streams for remote scp
            try {
                final Channel channel;
                String command = "scp -t " + getProperty("scp.target.path") + "/" + k + " ";
                System.out.println(command);
                try {
                    channel = session.openChannel("exec");
                    ((ChannelExec)channel).setCommand(command);
                    channel.connect();
                    command="C0644 " + v.length + " " + k + "\n";
                    System.out.println(command);
                    final OutputStream out = channel.getOutputStream();
                    out.write(command.getBytes());
                    out.flush();
                    out.write(v);
                    out.flush();
                    out.close();
                    channel.disconnect();
                } catch (JSchException e) {
                    e.printStackTrace();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }); //end forEach
        session.disconnect();
    } catch (JSchException e) {
        e.printStackTrace();
    }
}