среда, 5 декабря 2007 г.

stderr for `cmd`

Порой, программируя на ruby, нужно получить не только стандартный вывод для консольной команды, но и перехватить сообщения об ошибках. Для этого можно прямо использовать Open3, но для простых операций можно использовать обертку:

# extensions.rb

def `(cmd)
Open3.popen3(cmd) do |stdin, stdout, stderr|
out, err = [stdout, stderr].map &:readlines
{:short_out => out[0], :short_err => err[0], :out => out, :err => err, :all => out + err}
end
end

Примечание: для того, чтобы работала сокращенная форма
[stdout, stderr].map &:readlines

Необходимо, чтобы был определен метод to_proc для Symbol:
class Symbol
def to_proc
Proc.new { |obj, *args| obj.send(self, *args) }
end
end

Теперь можно работать с консольными командами следующим образом:
require 'extensions'

status = `rm non-existent-file`
p status[:short_err] # => "rm: non-existent-file: No such file or directory\n"
p status[:out] # => []


Или сразу вытаскивать нужные потоки:
out = `rm existent-file`[:out]
err = `rm non-existent-file`[:err]
all = `rm any-file`[:all]

Или, если использовать модифицированный хэш-аксессор из предыдущего поста:
short_out, full_out = `ln -s`[:short_out, :out]
out, err = `rm any-file`[:out, :short_err]



Также есть простой способ, с помощью которого без Open3 можно получить объединенный вывод для stderr и stdout. Для этого просто нужно указать в конце системного вызова 2>&1 - то есть дописать в стандартный вывод все полученные сообщения об ошибках.
Пример:
p `rm non-existent-file 2>&1` # >> "rm: non-existent-file: No such file or directory\n"