Ruby에서 Kernel # system 을 사용하여 명령을 호출하면 어떻게 출력합니까?
system("ls")
Ruby에서 Kernel # system 을 사용하여 명령을 호출하면 어떻게 출력합니까?
system("ls")
답변:
혼돈의 대답 을 조금 확장하고 명확하게하고 싶습니다 .
명령을 백틱으로 둘러싼 경우 system ()을 전혀 호출 할 필요가 없습니다. 백틱은 명령을 실행하고 출력을 문자열로 반환합니다. 그런 다음 값을 다음과 같이 변수에 할당 할 수 있습니다.
output = `ls`
p output
또는
printf output # escapes newline chars
ls #{filename}
.
command 2>&1
당신이 사용자 제공 값을 포함하는 문자열을 전달하는 모든 솔루션주의하십시오 system
, %x[]
등 안전하지! 실제로 안전하지 않은 의미 : 사용자가 컨텍스트에서 프로그램의 모든 권한으로 실행되도록 코드를 트리거 할 수 있습니다.
내가 말할 수있는 한 system
와 Open3.popen3
루비 1.8의 보안 / 탈출 변형을 제공 할. Ruby 1.9에서는 IO::popen
배열도 허용합니다.
모든 옵션과 인수를 이러한 호출 중 하나에 배열로 전달하면됩니다.
종료 상태뿐만 아니라 결과도 필요한 경우 Open3.popen3
:
require 'open3'
stdin, stdout, stderr, wait_thr = Open3.popen3('usermod', '-p', @options['shadow'], @options['username'])
stdout.gets(nil)
stdout.close
stderr.gets(nil)
stderr.close
exit_code = wait_thr.value
블록 형식은 stdin, stdout 및 stderr을 자동으로 닫습니다. 그렇지 않으면 명시 적 닫아야 합니다.
추가 정보 : Ruby에서 위생 쉘 명령 또는 시스템 호출 형성
gets
호출은 인수를 전달해야합니다. nil
그렇지 않으면 출력의 첫 번째 줄을 얻습니다. 예를 들어 stdout.gets(nil)
.
Open3.popen3
큰 문제가 없습니다 : 당신이 보유 할 수있는 파이프보다 표준 출력에 더 많은 데이터를 기록하는 서브 프로세스가있는 경우, 서브 프로세스가 일시 중지 도착 stderr.write
하고 프로그램에 갇혀됩니다 stdout.gets(nil)
.
레코드에 대해서만 (출력 및 작업 결과)를 원할 경우 다음을 수행 할 수 있습니다.
output=`ls no_existing_file` ; result=$?.success?
output=`ls no_existing_file 2>&1`; result=$?.success?
올 바르고 확실하게이 작업을 수행 할 수있는 간단한 방법을 사용하는 것 Open3.capture2()
, Open3.capture2e()
또는Open3.capture3()
.
신뢰할 수없는 데이터와 함께 사용하는 경우 루비의 백틱과 그 %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()
과 같은 함수를 래핑 할 수 있습니다 .
#
# 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')
capture2
, capture2e
및 capture3
도 가까운 그들 * 자동이야 수 std. (최소한, 나는 결코 내 문제에
Open3#popen2
, popen2e
그리고 popen3
미리 정의 된 블록 : ruby-doc.org/stdlib-2.1.1/libdoc/open3/rdoc/...
어떤 종류의 결과가 필요한지에 따라 system () 또는 % x []를 사용할 수 있습니다.
system ()은 명령이 발견되어 성공적으로 실행되면 true를 반환하고 그렇지 않으면 false를 반환합니다.
>> s = system 'uptime'
10:56 up 3 days, 23:10, 2 users, load averages: 0.17 0.17 0.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
제 제이 필드하여 블로그 게시물 [...] 간부 % X 및 상세 사용 시스템의 차이점을 설명한다.
인수를 이스케이프 해야하는 경우 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"
다른 방법은 다음과 같습니다.
f = open("|ls")
foo = f.read()
"ls"앞에있는 "pipe"문자가 열린 상태입니다. 이것은 또한 표준 출력을 읽을뿐만 아니라 프로그램 표준 입력으로 데이터를 공급하는데 사용될 수 있습니다.
반환 값이 필요한 경우 다음이 유용하다는 것을 알았습니다.
result = %x[ls]
puts result
나는 특히 내 컴퓨터에있는 모든 Java 프로세스의 pid를 나열하고 싶었고 이것을 사용했습니다.
ids = %x[ps ax | grep java | awk '{ print $1 }' | xargs]
백틱이나 팝을 사용하는 것이 종종 원하는 것이지만 실제로 실제로 묻는 질문에 대답하지는 않습니다. system
출력 을 캡처해야하는 유효한 이유가있을 수 있습니다 (자동 테스트의 경우). 작은 인터넷 검색 에서 다른 사람들의 이익을 위해 여기에 게시 할 것으로 생각 되는 답변을 찾았 습니다 .
테스트 에이 기능이 필요했기 때문에 실제 system
호출이 테스트중인 코드에 묻혀 있기 때문에 예제에서 블록 설정을 사용하여 표준 출력을 캡처합니다 .
require 'tempfile'
def capture_stdout
stdout = $stdout.dup
Tempfile.open 'stdout-redirect' do |temp|
$stdout.reopen temp.path, 'w+'
yield if block_given?
$stdout.reopen stdout
temp.read
end
end
이 방법은 실제 데이터를 저장하기 위해 임시 파일을 사용하여 주어진 블록의 출력을 캡처합니다. 사용법 예 :
captured_content = capture_stdout do
system 'echo foo'
end
puts captured_content
system
내부적으로 전화하는 것으로 통화를 교체 할 수 있습니다 system
. stderr
원하는 경우 비슷한 방법으로 캡처 할 수도 있습니다 .
직접 시스템 (...) 대체품으로 Open3.popen3 (...)을 사용할 수 있습니다.
추가 토론 : http://tech.natemurray.com/2007/03/ruby-shell-commands.html
나는 이것을 여기에서 찾지 못했기 때문에 그것을 추가하면 전체 출력을 얻는 데 약간의 문제가있었습니다.
백틱을 사용하여 STDERR을 캡처하려는 경우 STDERR을 STDOUT으로 리디렉션 할 수 있습니다.
출력 =`grep 호스트 / private / etc / * 2> & 1`
출처: http://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html