How can I log STDOUT and STDERR to a single file and show only STDERR in the console using Ruby?
I can do something like this in bash:
myCommand arg1 arg2 2>&1 >> myLogFolder/myLogFile.log | tee -a my开发者_如何学JAVALogFolder/myLogFile.log
I would like to be able to say this instead:
log.rb myLogFolder/myLogFile.log myCommand arg1 arg2
Using the log.rb script would accomplish two things:
- Result in a simpler statement with less redirection tricks and only a single specification of the log file.
- Create the log folder, if necessary.
I was looking at Ruby's popen and spawn options, but I don't see a way to split the STDERR stream to two destinations.
This Ruby script satisfies my needs, but maybe there is a better way.
logPath = ARGV[0]
logFolder = File.dirname(logPath)
command = ARGV.slice(1..-1).join(" ")
`mkdir -p #{logFolder}`
exec "#{command} 2>&1 >> #{logPath} | tee -a #{logPath}"
Try this article about implementing functionality similar to tee
using Ruby. You should be able to use that as a starting point for a pure-ruby (or at least exec
-free) implementation of your shell code.
You can use Open3
module (manual) It returns 3 objects: stdin, stdout, stderr
However, you are not able to preserve the order between stdout and stderr.
Example:
#include <stdio.h>
int main(int argc, char *argv[]) {
fprintf(stdout, "Normal output\n");
fprintf(stderr, "Error output\n");
fprintf(stdout, "Normal output\n");
}
It is captured as:
Normal output
Normal output
Error output
The only way how to preserve order is to run program twice. :-(
Sample ruby code:
#!/usr/bin/env ruby
require 'open3'
require 'pathname'
logfile = ARGV.first()
cmd = ARGV.drop(1).join(" ")
dir = Pathname(logfile).dirname
if not File.directory?(dir)
Dir.mkdir(dir)
end
file = File.open(logfile, "w")
stdin, stdout, stderr = Open3.popen3(cmd)
stdin.close()
file.puts(stdout.read())
error = stderr.read()
file.puts(error)
puts error
file.close
精彩评论