Fábio Queluci
Fábio Queluci

Reputation: 311

Pipe every bash output to file

Is there a way to pipe every output to a file, like monitoring the terminal to make a history of everything that is printed?

Also, when I use tee it seems to stop the output of the actual program. How can I change this? I want to maintain the normal output and just use a copy of it to save in the file.

Just as an example, I have this ruby script:

100.times do |i|
  puts i
end

And running $ruby script.rb | tee output.txt will save the output just fine, but it will only be printed after the whole thing runs.

Upvotes: 3

Views: 753

Answers (1)

Luke
Luke

Reputation: 513

Doing It With Bash

If you're using bash (which you are according to your tags), you could run script -a -c "ruby your_script.rb" -f your_file.out.

So, for example, if you wrote the following script, saved it as test.rb:

# test.rb
i = 0
while true do
    i += 1
    puts i
    sleep 1
end

And then ran script -a -c "ruby test.rb" -f test.out, you'd be able to refresh the file every time and see that it is incrementing one by one each second, even though the script is in media res; that is, still running. This is because it is "flushing" every output.

DRAWBACKS: You can only do this using the script command in bash. In this regard, you create a dependency; if script is not there, or it does not work as the script on my machine does, then this approach might not work at all. Always be wary of dependencies.

Doing It In Ruby Itself

Sometimes, you might not be running your script on bash and script may not be available. What then? Well, you can just create a method that prints to a file and then to the stdout, regardless of operating system. It will then flush the file every time to make sure it's up-to-date.

# print_to_file.rb
class PrintOutputToFile
  def self.file
    @@file ||= File.open("my_output.log", "w")
  end

  def self.puts! some_data
    file.write "#{some_data}\n"
    file.flush
    puts some_data
  end
end

i = 0
while true do
  i += 1
  PrintOutputToFile.puts! i
  sleep 1
end

DRAWBACKS: This will clear the file every time you run it. You might need to modify this little script if you want to keep a long-term log that saves over many runs. Also, this script will crash outright if you do not have file permissions. This is not meant to be used for production, but merely as a proof-of-concept for those interested in implementing a system like this.

Another Way To Do It In Ruby Itself

You can also do this by copying the old puts method into a proc, and then overriding the core puts method to write to the log file, but ending it with an invocation of the original puts method.

@@old_puts = method(:puts).to_proc
def puts(output)
  @@file ||= File.open("your_log.txt", "w")
  @@file.write "#{output}\n"
  @@old_puts.call output
  @@file.flush
end

DRAWBACKS: This is a step beyond hacky and I frankly would not recommend actually doing this unless you really, really know what you're doing and absolutely have to.

Upvotes: 3

Related Questions