下面的代码不打印tail -f
. 为什么?我怎样才能让它工作?
#myApp.rb
`ls` #works fine
`tail -f filename` #does not work. why?
-f
通过在执行的命令上使用 follow 选项tail
不会立即终止。
-f, --follow[={name|descriptor}]
output appended data as the file grows;
与使用相反,使用反引号(或%x
快捷方式)的想法system('...')
是这些语句返回已执行命令的输出。这样您就可以将结果存储在变量中:
dir_content = `ls`
tail -f
产生另一个进程,该进程继续写入标准输出而不会终止。因此,您的陈述永远不会结束。如果没有完成语句,则无法返回值。如果要查看和输出不断增长的文件,请参阅问题的解决方案:
或者,您可以像这样运行命令system
:
system('tail -f filename')
这里有什么区别?它不会返回命令的输出,而是返回 true(命令运行成功)、false(不成功)或 nil(命令执行失败)。由于命令的输出没有重定向到运行的 return 语句,tail -f
它会将内容打印到标准输出。
如果您可以将结果输出到标准输出,您可以简单地将其放入 Thread 块中。这样,不断增长的内容将filename
写入标准输出,您可以继续执行其他 Ruby 代码。
Thread.new { system('tail -f filename') }
如果您想要完全控制,请捕获输出以便将其存储以便在脚本的另一点进行检索,然后查看描述这种方法的以下问题的答案:
它基于创建和管理伪终端的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。
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>
回显手册页:
The echo utility writes any specified operands...followed
by a newline (`\n') character.
The reason it is not working is because the backticks make a separate System
call to run the commands within the backticks.
If you do ps aux | grep tail
, you will be able to see that the tail process is in fact running.
The code is not showing any output because its waiting for tail to terminate and proceed to next statements - its not hung because it is displaying the output of tail command.
Even in normal operation, you have to do a ctrl+C
to close the output of tail -f
command. The -f
is used to output appended data as the file grows, and hence tail -f
keeps running.
Try to do a kill -9 <pid_of_tail>
using the pid from ps aux | grep tail
. Then the remaining part of your ruby code will start outputting the result.
The ls
, tail without -f
calls work because they terminate on their own.
If you are interested in doing a tail from ruby, you can have a look at this file-tail
gem http://flori.github.io/file-tail/doc/index.html