Это очень ручная тема, спасибо. Класс для запуска команд и получения обратной связи великолепен в примере кода.
Иллюминат
3
Для будущих гуглеров. Если вы хотите узнать о других вызовах системных команд и их различиях, посмотрите этот SO-ответ .
Узбекджон
Ответы:
347
Я хотел бы немного расширить и уточнить ответ хаоса .
Если вы окружаете свою команду обратными галочками, то вам вообще не нужно (явно) вызывать system (). Обратные галочки выполняют команду и возвращают вывод в виде строки. Затем вы можете присвоить значение переменной следующим образом:
Что делать, если мне нужно дать переменную как часть моей команды? То есть, на что будет похожа система ("ls" + имя файла), когда следует использовать обратные метки?
Виджей Дев
47
Вы можете сделать оценку выражения так же , как вы бы с регулярными строками: ls #{filename}.
Крейг Уокер,
36
Этот ответ не рекомендуется: он вводит новую проблему неанизированного пользовательского ввода.
Dogweather
4
@ Dogweather: это может быть правдой, но отличается ли это от любых других методов?
Крейг Уокер,
20
если вы хотите захватить stderr, просто поставьте 2> & 1 в конце вашей команды. например, выход =command 2>&1
micred
243
Помните, что все решения, в которых вы передаете строку, содержащую предоставленные пользователем значения system,%x[] и т.д. небезопасны! На самом деле небезопасный означает: пользователь может запустить код для запуска в контексте и со всеми разрешениями программы.
Насколько я могу сказать, systemи Open3.popen3предоставить безопасный / экранирующий вариант в Ruby 1.8. В Ruby 1.9IO::popen также принимает массив.
Просто передайте каждый параметр и аргумент в виде массива одному из этих вызовов.
Если вам нужен не только статус выхода, но и результат, который вы, вероятно, хотите использовать Open3.popen3:
Это единственный ответ, который фактически отвечает на вопрос и решает проблему, не вводя новые (неантифицированный ввод).
Dogweather
2
Спасибо! Это такой ответ, на который я надеялся. Одно исправление: getsвызовы должны передавать аргумент nil, иначе мы просто получим первую строку вывода. Так, например stdout.gets(nil).
Кто-нибудь знает, что-то изменилось в Ruby 2.0 или 2.1? Будем благодарны за правки и комментарии ;-)
Simon Hürlimann
1
Я думаю, что в обсуждении Open3.popen3отсутствует главная проблема: если у вас есть подпроцесс, который записывает в стандартный вывод больше данных, чем может выдержать канал, подпроцесс приостанавливается stderr.write, и ваша программа застревает stdout.gets(nil).
Гагелло
165
Просто для записи, если вы хотите оба (вывод и результат операции), вы можете сделать:
Это только захватывает стандартный вывод, и стандартный вывод идет на консоль. Чтобы получить stderr, используйте: output=`ls no_existing_file 2>&1`; result=$?.success?
peterept
8
Этот ответ небезопасен и не должен использоваться - если команда не является константой, то синтаксис обратного удара может вызвать ошибку, возможно, уязвимость системы безопасности. (И даже если это константа, это, вероятно, заставит кого-то позже использовать ее для непостоянной и вызвать ошибку.) См . Ответ Саймона Хюрлиманна для правильного решения.
Грег Прайс
23
Слава Грегу Прайсу за понимание необходимости избегать пользовательского ввода, но неправильно писать этот ответ небезопасно. Упомянутый метод Open3 является более сложным и вводит больше зависимостей, и аргумент, что кто-то «будет использовать его для неконстантного позже», является бессмысленным. Правда, вы, вероятно, не использовали бы их в приложении на Rails, но для простого сценария системной утилиты без возможности ненадежного пользовательского ввода обратные помехи совершенно хороши, и никто не должен расстраиваться из-за их использования.
Использование обратных ссылок и %xпсевдонима ruby НЕ БЕЗОПАСНО ПОД ЛЮБЫМИ ОБСТОЯТЕЛЬСТВАМИ, если используется с ненадежными данными. Это ОПАСНО , просто и понятно:
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()в функцию, например:
## 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 eolrescueendend
Пример:
p system('foo')
p syscall('foo')
p system('which','foo')
p syscall('which','foo')
p system('which','which')
p syscall('which','which')
Получает следующее:
nilnilfalsefalse/usr/bin/which <— stdout from system('which','which')true<- p system('which','which')"/usr/bin/which"<- p syscall('which','which')
Это правильный ответ. Это также наиболее информативно. Единственное, чего не хватает, это предупреждения о закрытии std * s. Смотрите этот другой комментарий : require 'open3'; output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read } Обратите внимание, что блочная форма автоматически закрывает stdin, stdout и stderr, иначе они должны быть закрыты явно .
Питер Х. Болинг
@ PeterH.Boling: Лучше я знаю, то capture2, capture2eи capture3также близко их StD * с автоматически. (По крайней мере, я никогда не сталкивался с проблемой с моей стороны.)
Дени де Бернарди
без использования блочной формы кодовая база не сможет узнать, когда что-то должно быть закрыто, поэтому я очень сомневаюсь, что они закрываются. Вы, вероятно, никогда не сталкивались с проблемой, потому что ее закрытие не вызовет проблем в недолговечном процессе, и если вы достаточно часто перезапускаете длительный процесс, то otto не будет отображаться там, если вы не открываете std * s в цикл. В Linux существует высокий предел дескриптора файла, который вы можете использовать, но пока вы его не достигнете, вы не увидите «ошибки».
@Dennis de Barnardy Возможно, вы пропустили, что я ссылался на одну и ту же документацию по классу (хотя и для Ruby 2.0.0 и с другим методом. Ruby-doc.org/stdlib-2.1.1/libdoc/open3/rdoc/… из примера : `` `stdin, stdout, stderr, wait_thr = Open3.popen3 ([env,] cmd ... [, opts]) pid = wait_thr [: pid] # pid запущенного процесса ... stdin.close # stdin , stdout и stderr должны быть явно закрыты в этой форме. stdout.close stderr.close `` `Я просто цитирую документацию." # stdin, stdout и stderr должны быть явно закрыты в этой форме. "
Питер Х. Болинг
61
Вы можете использовать system () или% x [] в зависимости от того, какой результат вам нужен.
system () возвращает true, если команда была найдена и успешно выполнена, в противном случае - false.
>> s = system 'uptime'10:56 up 3 days,23:10,2 users, load averages:0.170.170.14=>true>> s.class=>TrueClass>> $?.class=>Process::Status
% x [..] с другой стороны сохраняет результаты команды в виде строки:
>> result =%x[uptime]=>"13:16 up 4 days, 1:30, 2 users, load averages: 0.39 0.29 0.23\n">> p result
"13:16 up 4 days, 1:30, 2 users, load averages: 0.39 0.29 0.23\n">> result.class=>String
В блоге Джея Филдса подробно объясняются различия между использованием system, exec и% x [..].
Спасибо за подсказку использования% x []. Это просто решило проблему, с которой я столкнулся, когда я использовал обратные тики в скрипте ruby в Mac OS X. При запуске того же скрипта на компьютере Windows с Cygwin он не работал из-за обратных тиков, но работал с% x [].
Хенрик Варн
22
Если вам нужно экранировать аргументы, в Ruby 1.9 IO.popen также принимает массив:
p IO.popen(["echo","it's escaped"]).read
В более ранних версиях вы можете использовать Open3.popen3 :
require "open3"Open3.popen3("echo","it's escaped"){|i, o| p o.read }
Если вам также нужно передать stdin, это должно работать как в 1.9, так и в 1.8:
out = IO.popen("xxd -p","r+"){|io|
io.print "xyz"
io.close_write
io.read.chomp
}
p out # "78797a"
Обратные пометки не производят вывод на терминале.
Мэй
3
Он не производит stderr, но дает стандартный вывод.
Николай Кондратенко
1
Он не пишет в стандартный вывод или стандартный вывод. Давайте попробуем этот пример ruby -e '%x{ls}'- обратите внимание, нет вывода. (К вашему сведению %x{}эквивалентны кавычкам .)
ocodo
Это сработало отлично. Использование shбудет выводить вывод на консоль (т.е. STDOUT), а также возвращать его. Это не
Джошуа Пинтер
19
Другой способ это:
f = open("|ls")
foo = f.read()
Обратите внимание, что это символ "pipe" перед открытием "ls". Это также может быть использовано для подачи данных в стандартный ввод программы, а также для считывания стандартного вывода.
Хотя использование обратных кавычек или popen часто является тем, что вам действительно нужно, на самом деле оно не отвечает на заданный вопрос. Могут быть веские причины для получения systemрезультатов (возможно, для автоматического тестирования). Немного погуглил ответ я решил опубликовать здесь для блага других.
Так как мне нужно было это для тестирования, мой пример использует настройку блока для захвата стандартного вывода, так как фактический systemвызов скрыт в тестируемом коде:
Для длительной команды это сохранит выходные данные в реальном времени. Вы также можете сохранить вывод, используя IO.pipe и перенаправить его из системы Kernel #.
Ответы:
Я хотел бы немного расширить и уточнить ответ хаоса .
Если вы окружаете свою команду обратными галочками, то вам вообще не нужно (явно) вызывать system (). Обратные галочки выполняют команду и возвращают вывод в виде строки. Затем вы можете присвоить значение переменной следующим образом:
или
источник
ls #{filename}
.command 2>&1
Помните, что все решения, в которых вы передаете строку, содержащую предоставленные пользователем значения
system
,%x[]
и т.д. небезопасны! На самом деле небезопасный означает: пользователь может запустить код для запуска в контексте и со всеми разрешениями программы.Насколько я могу сказать,
system
иOpen3.popen3
предоставить безопасный / экранирующий вариант в Ruby 1.8. В Ruby 1.9IO::popen
также принимает массив.Просто передайте каждый параметр и аргумент в виде массива одному из этих вызовов.
Если вам нужен не только статус выхода, но и результат, который вы, вероятно, хотите использовать
Open3.popen3
:Обратите внимание, что блочная форма автоматически закроет stdin, stdout и stderr, иначе они должны быть закрыты явно .
Более подробная информация здесь: Формирование команд санитарной оболочки или системных вызовов в Ruby.
источник
gets
вызовы должны передавать аргументnil
, иначе мы просто получим первую строку вывода. Так, напримерstdout.gets(nil)
.Open3.popen3
отсутствует главная проблема: если у вас есть подпроцесс, который записывает в стандартный вывод больше данных, чем может выдержать канал, подпроцесс приостанавливаетсяstderr.write
, и ваша программа застреваетstdout.gets(nil)
.Просто для записи, если вы хотите оба (вывод и результат операции), вы можете сделать:
источник
output=`ls no_existing_file 2>&1`; result=$?.success?
Простой способ сделать это правильно и надежно является использование
Open3.capture2()
,Open3.capture2e()
илиOpen3.capture3()
.Использование обратных ссылок и
%x
псевдонима ruby НЕ БЕЗОПАСНО ПОД ЛЮБЫМИ ОБСТОЯТЕЛЬСТВАМИ, если используется с ненадежными данными. Это ОПАСНО , просто и понятно:system
Функция, в отличие от этого , сбегает аргументы должным образом , если они используются правильно :Проблема в том, что он возвращает код выхода вместо вывода, а захват последнего является запутанным и грязным.
Лучший ответ в этой теме на данный момент упоминает Open3, но не функции, которые лучше всего подходят для этой задачи.
Open3.capture2
,capture2e
Иcapture3
работа , какsystem
, но возвращает два или три аргумента:Другой упоминает
IO.popen()
. Синтаксис может быть неуклюжим в том смысле, что ему нужен массив в качестве входных данных, но он тоже работает:Для удобства вы можете заключить
Open3.capture3()
в функцию, например:Пример:
Получает следующее:
источник
require 'open3'; output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read }
Обратите внимание, что блочная форма автоматически закрывает stdin, stdout и stderr, иначе они должны быть закрыты явно .capture2
,capture2e
иcapture3
также близко их StD * с автоматически. (По крайней мере, я никогда не сталкивался с проблемой с моей стороны.)Open3#popen2
,popen2e
иpopen3
с предопределенным блока: ruby-doc.org/stdlib-2.1.1/libdoc/open3/rdoc/...Вы можете использовать system () или% x [] в зависимости от того, какой результат вам нужен.
system () возвращает true, если команда была найдена и успешно выполнена, в противном случае - false.
% x [..] с другой стороны сохраняет результаты команды в виде строки:
В блоге Джея Филдса подробно объясняются различия между использованием system, exec и% x [..].
источник
Если вам нужно экранировать аргументы, в Ruby 1.9 IO.popen также принимает массив:
В более ранних версиях вы можете использовать Open3.popen3 :
Если вам также нужно передать stdin, это должно работать как в 1.9, так и в 1.8:
источник
Вы используете backticks:
источник
ruby -e '%x{ls}'
- обратите внимание, нет вывода. (К вашему сведению%x{}
эквивалентны кавычкам .)sh
будет выводить вывод на консоль (т.е. STDOUT), а также возвращать его. Это неДругой способ это:
Обратите внимание, что это символ "pipe" перед открытием "ls". Это также может быть использовано для подачи данных в стандартный ввод программы, а также для считывания стандартного вывода.
источник
Я обнаружил, что следующее полезно, если вам нужно возвращаемое значение:
Я специально хотел перечислить pids всех процессов Java на моем компьютере и использовал это:
источник
Как уже объяснил Саймон Хюрлиманн , Open3 безопаснее, чем обратные трюки и т. Д.
Обратите внимание, что блочная форма автоматически закроет stdin, stdout и stderr, иначе они должны быть закрыты явно .
источник
Хотя использование обратных кавычек или popen часто является тем, что вам действительно нужно, на самом деле оно не отвечает на заданный вопрос. Могут быть веские причины для получения
system
результатов (возможно, для автоматического тестирования). Немного погуглил ответ я решил опубликовать здесь для блага других.Так как мне нужно было это для тестирования, мой пример использует настройку блока для захвата стандартного вывода, так как фактический
system
вызов скрыт в тестируемом коде:Этот метод захватывает любой вывод в данном блоке, используя временный файл для хранения фактических данных. Пример использования:
Вы можете заменить
system
звонок на все, что звонит внутриsystem
. Вы также можете использовать аналогичный метод для захвата,stderr
если хотите.источник
Если вы хотите, чтобы вывод перенаправлялся в файл с помощью
Kernel#system
, вы можете изменить дескрипторы следующим образом:перенаправить stdout и stderr в файл (/ tmp / log) в режиме добавления:
system('ls -al', :out => ['/tmp/log', 'a'], :err => ['/tmp/log', 'a'])
Для длительной команды это сохранит выходные данные в реальном времени. Вы также можете сохранить вывод, используя IO.pipe и перенаправить его из системы Kernel #.
источник
В качестве прямой замены системы (...) вы можете использовать Open3.popen3 (...)
Дальнейшее обсуждение: http://tech.natemurray.com/2007/03/ruby-shell-commands.html
источник
Я не нашел этого здесь, поэтому добавляя его, у меня были некоторые проблемы с получением полного вывода.
источник: http://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html
источник
источник