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

How can I escape doublequotes when using SCP with JSch?

提问人:du-it 提问时间:10/9/2017 最后编辑:du-it 更新时间:10/10/2017 访问量:904

问:

在 Windows 系统上安装了 SSH,并与远程主机 (Linux) 交换密钥。我想在 Windows 机器上运行 JAR 到 Linux 机器的 SCP 文件。 在命令行上执行此操作

"C:/Program Files (X86)/ICW/bin/SCP.exe" -i .ssh/id_rsa <filename> [email protected]:/target/path/<filename>

它工作得很好(使用双引号)。

但是当我使用 JSch 从 JAR 中运行 SCP 时,我在索引处收到一个 sun.nio.file.InvalidPathException: Illegal char <“> ...

目的是我必须以编程方式从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<String,byte[]> 和 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 主机上。 如果我在这样做时完全误解,我将不胜感激任何专业知识的转移,以学习如何以正确的方式做到这一点。:-)

java 引用 scp jsch java.nio.file

评论

1赞 Kenster 10/9/2017
编辑您的问题以包含您程序中的相关源代码。
0赞 Martin Prikryl 10/10/2017
您确定要在服务器上执行吗?为什么?scp.exe
0赞 Martin Prikryl 10/10/2017
所以你在 Windows 机器上运行 Java 代码,对吧?那么,如果你在本地运行它,你为什么要通过JSch运行它呢?如果要从本地 (Windows) 计算机传输文件,请使用本机 JSch SCP(或更好的 SFTP)实现。其实你的问题有点道理。而且我真的不相信你从 JSch 那里得到。这是当地的例外。向我们展示您的代码。scp.exeInvalidPathException
0赞 Martin Prikryl 10/10/2017
我们需要知道,到底是什么 + 你有什么充分的理由使用 SCP 吗?这是一个遗留协议。您最好使用 SFTP。+ 无论如何,您的代码在远程 Linux 计算机上使用 SSH 运行该命令。因此,如果您确实尝试在 Linux 机器上运行,这显然完全是无稽之谈。而你的根本问题与“双引号”无关,而是与对你正在做的事情的完全误解有关。setCommand(command)C:/Program Files (X86)/ICW/bin/SCP.exe
0赞 du-it 10/10/2017
当然,我不想在 Linux 机器上运行 Windows SCP,但我必须从 JAR 中运行 Windows SCP,因为 JAR 是在 Windows 机器上执行的。

答:

1赞 Martin Prikryl 10/10/2017 #1

您显然正在尝试启动从本地 Windows 计算机到远程 Linux 计算机的 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 客户端组合在一起也是没有意义的。

1赞 du-it 10/10/2017 #2

好的,马丁,谢谢你引导我朝着正确的方向前进。可能我会检查并尝试使用 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();
    }
}