Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

back-tick in ruby does not work with tail -f command

Tags:

logging

ruby

tail

The code below does not print the output of tail -f. Why? How can I make it work?

#myApp.rb
`ls`               #works fine
`tail -f filename`      #does not work. why?
like image 874
Bhuvan Avatar asked Sep 01 '13 07:09

Bhuvan


People also ask

What are Backticks in ruby?

Backticks (``) call a system program and return its output. As opposed to the first approach, the command is not provided through a string, but by putting it inside a backticks pair.

What is ruby command?

Ruby command is a free and open source programming language; it is flexible and is feature rich. As the name suggests, ruby indeed is a jewel language which comes at a very low entry cost. Its plug and play capability and also easily readable syntax makes it very user-friendly.


1 Answers

By using the follow option -f on tail the executed command will not immediately terminate.

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

The idea of using backticks (or the %x shortcut), as opposed to using system('...') is that these statements return the output of the executed commands. This way you could store the result in a variable:

dir_content = `ls`

tail -f spawns another process, this process keeps on writing to standard output without terminating. Hence, your statement is never finished. Without finishing the statement, a value cannot be returned. If you want to watch and output of a growing file, see the solution to the question:

Watch/read a growing log file.

Alternatively, you could run the command through system like so:

system('tail -f filename')

What is the difference here? Instead of returning the outpout of the command, it will return true (command run successfully), false (unsuccessful) or nil (command execution failed). Due to the fact, that the output of the command is not redirected to the return statement running tail -f it will print the content to standard output.

If you are fine with getting the results to standard output you can simply put it into a Thread block. This way the growing content of filename is written to standard output and you can continue to execute other Ruby code.

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

If you want to have complete control, capture the output in order to store it for retrieval at another point of your script then have a look at the answer to the following question which describes such an approach:

Continuously read from STDOUT of external process in Ruby

It is based on the PTY module which creates and manages pseudo terminals. Basically you can spawn another terminal through this module. The code could then look like so:

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

Finally, there is also a socket approach. Open a socket in Bash or by using socat, pipe the output of tail -f to the socket and read in Ruby from the socket.

like image 187
Konrad Reiche Avatar answered Sep 20 '22 00:09

Konrad Reiche