Ruby back-tick not working with tail -f command

The code below does not output tail -f output. What for? How can I make it work?

 #myApp.rb `ls` #works fine `tail -f filename` #does not work. why? 
+6
source share
3 answers

Using the following -f on tail option, a executed command will not immediately end.

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

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

 dir_content = `ls` 

tail -f spawns another process, this process continues to write to standard output without interruption. Therefore, your expression never ends. Without completion of the instruction, the value cannot be returned. If you want to watch and display a growing file, see Solution:

View / read a growing log file .

Alternatively, you can run the command through system as follows:

 system('tail -f filename') 

What is the difference? Instead of returning the outpout of the command, it will return true (the command starts successfully), false (unsuccessfully) or nil (the command did not complete). Due to the fact that the output of the command is not redirected to the return statement running tail -f , it will print the contents to standard output.

If you're good at getting results on standard output, you can just put it in a Thread block. This way, the growing content of filename written to standard output, and you can continue to run other Ruby code.

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

If you want complete control, write down the output to save it for searching at another point in your script, and then look at the answer to the following question that describes this approach:

Read continuously from STDOUT of an external process in Ruby

It is based on the PTY module, which creates and manages pseudo-terminals. Basically, you can create another terminal through this module. Then the code might look like this:

 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 the socket in Bash or using socat , output the tail -f output to the socket and read it in Ruby from the socket.

+7
source

1) According to backticks docs docs,

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

So if you write:

 puts `some command` 

then ruby ​​should print any commands.

2) According to the tail pages of man:

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

But the team:

 puts `tail -f some_file` 

Doesn’t print anything, despite the fact that it runs:

 $ tail -f some_file 

sends the output to standard output. Tail man pages also say

 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]. 

What does it mean? And how does this relate to the problem?

By running the program:

 puts `tail -f some_file` 

... freezes and doesn’t output anything, you can conclude that the backticks method should try to capture the entire output of the command. In other words, the backticks method does not read line by line from the standard output of the command, i.e. Oriented by input lines. Instead, the backticks method reads an input-oriented file, i.e. File at a time. And since the tail -f some_file never finishes writing to standard output, the backticks method never reads the end of the file and it hangs while it waits for eof.

3) However, reading the command displays the file at a time, this is not the only way to read the output of the command. You can also read the command, output the string at a time using 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 man pages:

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

The reason it doesn't work is because callbacks make a separate call to System to run commands in reverse loops. If you do ps aux | grep tail ps aux | grep tail , you can see that the tail process is actually running.

The code does not show any output, because it is waiting for the tail to complete and moving on to the next operators - it does not hang because it displays the output of the tail command.

Even during normal operation, you must do ctrl+C to close the output of the tail -f command. -f used to output the added data as the file grows and therefore tail -f continues to work. Try making kill -9 <pid_of_tail> using pid from ps aux | grep tail ps aux | grep tail . Then the rest of your ruby ​​code will begin to output the result.

Calling ls , tail without -f works because they end on their own.

If you are interested in creating a ruby ​​tail, you can look at this file-tail gem http://flori.imtqy.com/file-tail/doc/index.html

0
source

Source: https://habr.com/ru/post/952912/


All Articles