通过管道调用sftp:不返回提示

时间:2014-05-05 13:31:21

标签: ruby unix process pipe

我尝试使用SFTP通过使用管道调用SFTP进程来复制文件(在Linux上)。

我首先试着打电话给#34; ls"但我的问题是我不知道命令输出何时完成,所以我知道我可以发送另一个命令,并检查第一个命令的输出。

我希望sftp在命令完成后再次打印出它的提示,如下所示:

sftp>ls
...output...
sftp>

这样我知道命令何时完成。然而,发生的事情是我得到......输出...但我从未收到新的sftp>提示。

我不明白它是如何在命令行中运行的,而不是在手动运行管道时。另外this PHP article建议这种等待sftp>的方法。提示。

我在java和ruby中编写了两个实现,并且都显示了相同的行为。我小心不使用缓冲读卡器,因为我知道这可能会导致问题,因为sftp在" sftp>"之后没有放回车。提示,但无济于事。

如果我在shell echo ls | sftp <parameters>中运行,那么它会输出并退出,而不会给出新的提示。那实际上是一个足够好的&#34;我的行为。

我知道我可以编写我想要在文件中运行的所有命令,并通过命令参数将该文件提供给sftp,但我发现管道方法比使用临时文件更可行且更少笨重。

这是Java实现:

String[] cmd = new String[]{"sftp", Config.getSshTargetUsername()
      + "@" + Config.getSshTargetHost()};

Process p = Runtime.getRuntime().exec(cmd);
InputStreamReader inp = new InputStreamReader(p.getInputStream());
OutputStreamWriter out = new OutputStreamWriter(p.getOutputStream());

out.write("ls\n");
out.flush();

StringBuilder read = new StringBuilder();
char[] buf = new char[256];
while (true) {
    int bytes = inp.read(buf);
    read.append(buf, 0, bytes);
    System.out.println(read);
    System.out.flush();
}

红宝石:

require 'open3'
cmd = "sftp #{username}@#{host}"
Open3.popen2e(cmd) do |stdin, stdout_err, wait_thr|
  stdin.puts "ls"
  while chr = stdout_err.getc
    putc chr
  end
end

更新:现在我意识到如果我启动bash,例如我得到完全相同的行为。所以关于管道的一般情况,并非特别与sftp相关。

2 个答案:

答案 0 :(得分:1)

好的,我找到了解释和正确答案。

在sftp源代码中我看到了:

if (interactive)
    printf("sftp> ");

稍早一点:

interactive = !batchmode && isatty(STDIN_FILENO);

isatty()是关键。这样,sftp可以检测它是由现场用户,tty还是其他应用程序执行。

所以我必须让它相信它是由现场用户发布的。一个快速的谷歌出现了这个链接: Trick an application into thinking its stdin is interactive, not a pipe

我使用script启动sftp,从我的测试中看起来效果很好......这就是我说的正确方法!关于它似乎在PHP中开箱即用的事实,我想PHP在启动外部进程时开箱即用。

答案 1 :(得分:0)

所以现在我放弃了(已经手动启动sftp从使用正确的sftp库退出,我没有更多的时间用于此),现在我正在用管道直接启动解决方案。这样,当sftp完成处理命令后退出。我为每个操作启动了一个新的sftp,但它是可行的。

在java中:

String[] cmd = new String[] { "/bin/sh", "-c", 
      "echo ls | sftp " + Config.getSshTargetUsername() + "@" +
      Config.getSshTargetHost() };

Process p = Runtime.getRuntime().exec(cmd);
BufferedReader inp = new BufferedReader(new
      InputStreamReader(p.getInputStream()));
String line;
while ((line = inp.readLine()) != null) {
    System.out.println(line);
}

在红宝石中:

require 'open3'
cmd = 'echo ls | sftp #{username}@#{host}'
Open3.popen2e(cmd) do |stdin, stdout_err, wait_thr|
  while l = stdout_err.readline
    puts l
  end
end

我仍然很好奇为什么我的第一个解决方案不起作用,我希望有一天能有人回答。