Простой способ сделать это правильно и безопасно - использовать Open3.capture2()
, Open3.capture2e()
или Open3.capture3()
.
Использование обратных ссылок ruby и его псевдонима %x
НЕ БЕЗОПАСНЫ ПОД НИКАКИМИ ОБСТОЯТЕЛЬСТВАМИ при использовании с ненадежными данными Это ОПАСНО , просто и просто:
untrusted = "; date; echo"
out = `echo #{untrusted}` # BAD
untrusted = '"; date; echo"'
out = `echo "#{untrusted}"` # BAD
untrusted = "'; date; echo'"
out = `echo '#{untrusted}'` # BAD
Функция system
, напротив, должным образом экранирует аргументы , если используется правильно :
ret = system "echo #{untrusted}" # BAD
ret = system 'echo', untrusted # good
Проблема в том, что вместо вывода возвращается код выхода, а захват последнего запутан и запутан.
Лучший ответ в этой теме на данный момент упоминает Open3, но не функции, которые лучше всего подходят для этой задачи. Open3.capture2
, capture2e
и capture3
работают как system
, но возвращают два или три аргумента:
out, err, st = Open3.capture3("echo #{untrusted}") # BAD
out, err, st = Open3.capture3('echo', untrusted) # good
out_err, st = Open3.capture2e('echo', untrusted) # good
out, st = Open3.capture2('echo', untrusted) # good
p st.exitstatus
Другой упоминает IO.popen()
. Синтаксис может быть неуклюжим в том смысле, что ему нужен массив в качестве входных данных, но он тоже работает:
out = IO.popen(['echo', untrusted]).read # good
Для удобства вы можете обернуть Open3.capture3()
в функцию, например ::1010 *
#
# Returns stdout on success, false on failure, nil on error
#
def syscall(*cmd)
begin
stdout, stderr, status = Open3.capture3(*cmd)
status.success? && stdout.slice!(0..-(1 + $/.size)) # strip trailing eol
rescue
end
end
Пример: * * тысяча сорок шесть
p system('foo')
p syscall('foo')
p system('which', 'foo')
p syscall('which', 'foo')
p system('which', 'which')
p syscall('which', 'which')
Получает следующее:
nil
nil
false
false
/usr/bin/which <— stdout from system('which', 'which')
true <- p system('which', 'which')
"/usr/bin/which" <- p syscall('which', 'which')