【问题标题】:back-tick in ruby does not work with tail -f commandruby 中的反引号不适用于 tail -f 命令
【发布时间】:2013-09-01 07:05:29
【问题描述】:

下面的代码不会打印tail -f 的输出。为什么?我怎样才能让它发挥作用?

#myApp.rb
`ls`               #works fine
`tail -f filename`      #does not work. why?

【问题讨论】:

  • "filename" 是文件的文字名称(或仅用于您的问题的虚拟名称)?或者它是一个包含字符串的变量的名称?
  • filename 是问题的假人

标签: ruby logging tail


【解决方案1】:

通过在tail 上使用跟随选项-f,执行的命令不会立即终止。

-f, --follow[={name|descriptor}]
  output appended data as the file grows;

使用反引号(或%x 快捷方式),而不是使用system('...') 的想法是这些语句返回执行命令的输出。这样您就可以将结果存储在变量中:

dir_content = `ls`

tail -f 生成另一个进程,该进程继续写入标准输出而不会终止。因此,您的陈述永远不会结束。如果没有完成语句,则无法返回值。如果您想观看和输出一个不断增长的文件,请参阅问题的解决方案:

Watch/read a growing log file.

或者,您可以像这样通过system 运行命令:

system('tail -f filename')

这里有什么区别?它不会返回命令的输出,而是返回 true(命令运行成功)、false(不成功)或 nil(命令执行失败)。由于命令的输出没有重定向到运行tail -f的返回语句,它会将内容打印到标准输出。

如果您可以将结果输出到标准输出,您可以简单地将其放入 Thread 块中。这样filename 不断增长的内容将写入标准输出,您可以继续执行其他 Ruby 代码。

Thread.new { system('tail -f filename') }

如果您想要完全控制,请捕获输出以便将其存储以便在脚本的另一点进行检索,然后查看描述这种方法的以下问题的答案:

Continuously read from STDOUT of external process in Ruby

它基于创建和管理伪终端的PTY 模块。基本上你可以通过这个模块产生另一个终端。代码可能如下所示:

require 'pty'
cmd = "tail -f filename" 
begin
  PTY.spawn(cmd) do |stdin, stdout, pid|
    begin
      # Do something with the output here: just printing to demonstrate it
      stdin.each { |line| print line }
    rescue Errno::EIO
      puts "Errno:EIO error, but this probably just means " +
            "that the process has finished giving output"
    end
  end
rescue PTY::ChildExited
  puts "The child process exited!"
end

最后,还有一种套接字方法。在 Bash 中打开一个套接字,或者使用 socat,将 tail -f 的输出通过管道传输到套接字并从套接字读取 Ruby。

【讨论】:

  • 你的帖子一团糟,只描述了发生的事情,而不是解释为什么会发生。操作人员已经知道会发生什么。否决。为您的帖子点赞的人应该解释他们点赞的原因。你的推理是这样的:如果你执行一个不返回值的命令,它就不能写入标准输出;但是如果你使用 ruby​​ 的 system() 方法来执行一个不返回值的命令,它将写入标准输出。嗯?
  • 我没有以任何形式说明,如果你执行一个不返回值的命令,它就不能写入标准输出。此外,我没有从问题中看到 OP 已经知道什么以及他不知道什么。不过感谢您的评论,我会尝试改进配方。
  • platzhirsch 写道: 如果没有终止,则不会给出返回值,因此不会打印任何内容。 那是什么意思呢?或者换一种说法,一个命令的返回值是否与命令是否可以写入标准输出有关?
  • @7stud 好的,谢谢。我已经编辑了答案。希望它能解决问题。
  • @7stud 这对我来说没有任何意义。标准输出读取永远不会完成,因此无法通过运行命令给出返回值。如果通过使用反引号,将返回 IO 对象或类似的东西,并且如果 puts 将接受并且 IO 对象并不断从中读取,那么这将是有意义的。然而,事实并非如此。
【解决方案2】:

1) 根据反引号文档,

`cmd`
Returns the standard output of running cmd in a subshell.

所以如果你写:

puts `some command`

那么 ruby​​ 应该打印一些命令输出。

2) 根据尾部手册页:

The tail utility displays the contents of file...to the standard
output.

但是命令:

puts `tail -f some_file`

尽管正在运行,但不打印任何内容:

$ tail -f some_file

将输出发送到标准输出。尾部手册页也说

The -f option causes tail to not stop when
end of file is reached, but rather to wait
for additional data to be appended to the
[the file].

这是什么意思?它与手头的问题有什么关系?

因为运行程序:

puts `tail -f some_file`

...挂起并且不输出任何内容,您可以推断反引号方法必须尝试捕获命令的整个输出。换句话说,反引号方法不会从命令的标准输出中逐行读取,即面向行的输入。相反,反引号方法读取面向文件的输入,即一次读取一个文件。 因为命令tail -f some_file 永远不会完成对标准输出的写入,所以反引号方法永远不会读取文件结尾,它会在等待时挂起为 eof。

3) 然而,读取命令的输出一次一个文件并不是读取命令输出的唯一方法。您还可以使用 popen() 一次读取一行命令的输出:

my_prog.rb:

IO.popen('tail -f data.txt') do |pipe| 
  while line = pipe.gets do  #gets() reads up to the next newline, then returns
    puts line
  end
end


--output(terminal window 1):--
$ ruby my_prog.rb 
A
B
C
<hangs>

--output(terminal window 2):--
echo 'D' >> data.txt

--output(terminal window 1):--
A
B
C
D
<hangs>

echo 手册页:

The echo utility writes any specified operands...followed
by a newline (`\n') character.

【讨论】:

    【解决方案3】:

    它不起作用的原因是反引号单独调用System 来运行反引号内的命令。 如果您执行ps aux | grep tail,您将能够看到尾部进程实际上正在运行。

    代码没有显示任何输出,因为它正在等待 tail 终止并继续执行下一条语句 - 它没有挂起,因为它正在显示 tail 命令的输出。

    即使在正常操作中,您也必须执行ctrl+C 来关闭tail -f 命令的输出。 -f 用于在文件增长时输出附加数据,因此tail -f 会继续运行。 尝试使用来自ps aux | grep tail 的 pid 执行kill -9 &lt;pid_of_tail&gt;。然后你的 ruby​​ 代码的剩余部分将开始输出结果。

    lstail without -f 调用可以正常工作,因为它们会自行终止。

    如果你有兴趣从 ruby​​ 做一个尾巴,你可以看看这个file-tail gem http://flori.github.io/file-tail/doc/index.html

    【讨论】:

      猜你喜欢
      • 2015-05-29
      • 2015-12-26
      • 2011-09-12
      • 1970-01-01
      • 2022-10-14
      • 2018-03-22
      • 1970-01-01
      • 1970-01-01
      • 2021-11-07
      相关资源
      最近更新 更多