Ruby에서 system () 호출의 출력 얻기


309

Ruby에서 Kernel # system 을 사용하여 명령을 호출하면 어떻게 출력합니까?

system("ls")


이것은 매우 손 실입니다. 감사합니다. 명령을 실행하고 피드백을받는 클래스는 샘플 코드에서 훌륭합니다.
ylluminate

3
미래의 구글 직원들에게. 다른 시스템 명령 호출과 그 차이점에 대해 배우려면 이 SO 답변을 참조하십시오 .
우즈벡 존

답변:


347

혼돈의 대답 을 조금 확장하고 명확하게하고 싶습니다 .

명령을 백틱으로 둘러싼 경우 system ()을 전혀 호출 할 필요가 없습니다. 백틱은 명령을 실행하고 출력을 문자열로 반환합니다. 그런 다음 값을 다음과 같이 변수에 할당 할 수 있습니다.

output = `ls`
p output

또는

printf output # escapes newline chars

4
명령의 일부로 변수를 제공해야하는 경우 어떻게해야합니까? 즉, 백틱을 사용할 때 system ( "ls"+ filename)과 같은 것은 무엇입니까?
Vijay Dev

47
정규 문자열과 마찬가지로 표현식 평가를 수행 할 수 있습니다 ls #{filename}.
Craig Walker

36
이 답변은 바람직하지 않습니다. 비위생 사용자 입력의 새로운 문제를 소개합니다.
Dogweather

4
@Dogweather : 사실 일 수도 있지만 다른 방법과 다른 점이 있습니까?
Craig Walker

20
stderr를 캡처하려면 명령 끝에 2> & 1을 입력하십시오. 예 : output =command 2>&1
micred

243

당신이 사용자 제공 값을 포함하는 문자열을 전달하는 모든 솔루션주의하십시오 system, %x[]등 안전하지! 실제로 안전하지 않은 의미 : 사용자가 컨텍스트에서 프로그램의 모든 권한으로 실행되도록 코드를 트리거 할 수 있습니다.

내가 말할 수있는 한 systemOpen3.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에서 위생 쉘 명령 또는 시스템 호출 형성


26
이것은 실제로 질문에 대답하고 새로운 질문을 도입하지 않고 문제를 해결하는 유일한 답변입니다 (위생되지 않은 입력).
Dogweather

2
감사! 이것은 내가 기대했던 일종의 대답입니다. 한 가지 수정 : gets호출은 인수를 전달해야합니다. nil그렇지 않으면 출력의 첫 번째 줄을 얻습니다. 예를 들어 stdout.gets(nil).
Greg Price

3
stdin, stdout 및 stderr은 비 블록 형식으로 명시 적 으로 닫아야합니다 .
Yarin

Ruby 2.0 또는 2.1에서 변경된 사항이 있는지 아는 사람이 있습니까? 편집 또는 의견을
부탁

1
나는 토론 주위에 있다고 생각 Open3.popen3큰 문제가 없습니다 : 당신이 보유 할 수있는 파이프보다 표준 출력에 더 많은 데이터를 기록하는 서브 프로세스가있는 경우, 서브 프로세스가 일시 중지 도착 stderr.write하고 프로그램에 갇혀됩니다 stdout.gets(nil).
hagello

165

레코드에 대해서만 (출력 및 작업 결과)를 원할 경우 다음을 수행 할 수 있습니다.

output=`ls no_existing_file` ;  result=$?.success?

4
이것이 바로 내가 찾던 것입니다. 감사합니다.
jdl

12
그것은 stdout 만 캡처하고 stderr은 콘솔로 이동합니다. stderr를 얻으려면 다음을 사용하십시오. output=`ls no_existing_file 2>&1`; result=$?.success?
peterept

8
이 답변은 안전하지 않으며 사용해서는 안됩니다. 명령이 상수가 아닌 경우 백틱 구문은 버그, 아마도 보안 취약점을 일으킬 수 있습니다. (그리고 그것이 일정하더라도 나중에 누군가가 상수가 아닌 것을 사용하여 버그를 일으킬 수 있습니다.) 올바른 해결책 은 Simon Hürlimann의 답변 을 참조하십시오 .
그렉 가격

23
사용자 입력을 피해야 할 필요성에 대해 이해하기 위해 Greg Price에게 도움이 되긴하지만이 답변이 안전하지 않다고 말하는 것은 옳지 않습니다. 언급 된 Open3 방법은 더 복잡하고 더 많은 의존성을 제공하며, 누군가가 "일정하지 않은 상태에서이 방법을 사용할 것"이라는 주장은 짚맨입니다. 사실, Rails 앱에서 사용하지는 않지만 신뢰할 수없는 사용자 입력이없는 간단한 시스템 유틸리티 스크립트의 경우 백틱이 완벽하고 아무도 사용에 대해 기분 나쁘게 느끼지 않아야합니다.
sbeam

69

올 바르고 확실하게이 작업을 수행 할 수있는 간단한 방법을 사용하는 것 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, capture2ecapture3작업 같은 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')

2
이것이 정답입니다. 또한 가장 유익한 정보입니다. 유일하게 누락 된 것은 std * s 종료에 대한 경고입니다. 이 다른 의견을 참조하십시오 : require 'open3'; output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read } 블록 형식은 stdin, stdout 및 stderr을 자동으로 닫습니다. 그렇지 않으면 명시 적 으로 닫아야 합니다.
피터 H. 볼링

@ PeterH.Boling : 최고의 내가 알고 있어요는 capture2, capture2ecapture3도 가까운 그들 * 자동이야 수 std. (최소한, 나는 결코 내 문제에
부딪치지

블록 양식을 사용하지 않고 거기에 뭔가가 폐쇄되어야 할 때 알아야 할 코드베이스에 대한 방법이 없다 그래서 나는 매우 그들이 폐쇄되고 의심한다. 프로세스를 닫지 않으면 수명이 짧은 프로세스에서 문제가 발생하지 않으므로 장기 실행 프로세스를 자주 다시 시작하면 std * s를 열지 않으면 otto가 표시되지 않습니다. 루프. 리눅스는 파일 디스크립터 제한이 높기 때문에 적중 할 수 있지만,이를 때까지 "버그"를 볼 수 없습니다.
피터 H. 볼링

2
@ PeterH.Boling : 아니요, 소스 코드를 참조하십시오. 기능은 단지 주변 래퍼 Open3#popen2, popen2e그리고 popen3미리 정의 된 블록 : ruby-doc.org/stdlib-2.1.1/libdoc/open3/rdoc/...
데니스 Bernardy에 드

1
@Dennis 드 Barnardy 아마도 당신은 내가 루비 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.close stderr.close을```난 그냥 문서를 인용했다 "#의 표준 입력, 표준 출력 및 표준 오류는이 양식에 명시 적으로 닫아야합니다."..
피터 H. Boling

61

어떤 종류의 결과가 필요한지에 따라 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 및 상세 사용 시스템의 차이점을 설명한다.


2
% x []를 사용해 주셔서 감사합니다. Mac OS X의 루비 스크립트에서 백틱을 사용한 문제를 해결했습니다. Cygwin이 설치된 Windows 컴퓨터에서 동일한 스크립트를 실행할 때 백틱으로 인해 실패했지만 % x []에서 작동했습니다.
Henrik Warne

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"

감사! 이것은 완벽 해요.
Greg Price

21

백틱을 사용합니다.

`ls`

5
백틱은 터미널에서 출력을 생성하지 않습니다.
메이

3
stderr을 생성하지 않지만 stdout을 제공합니다.
Nickolay Kondratenko

1
stdout 또는 stderr에 쓰지 않습니다. 이 예제를 시도해보십시오 ruby -e '%x{ls}'-출력은 없습니다. (fyi %x{}는 백틱과 동일합니다.)
ocodo

이것은 훌륭하게 작동했습니다. 를 사용 sh하면 출력을 콘솔 (예 : STDOUT)로 에코하고 반환합니다. 그렇지 않습니다.
조슈아 핀터

19

다른 방법은 다음과 같습니다.

f = open("|ls")
foo = f.read()

"ls"앞에있는 "pipe"문자가 열린 상태입니다. 이것은 또한 표준 출력을 읽을뿐만 아니라 프로그램 표준 입력으로 데이터를 공급하는데 사용될 수 있습니다.


방금 'true'의 공식 반환 값이 아닌 json을 읽기 위해 aws cli 명령의 표준 출력을 읽는 데 사용했습니다.
kraftydevil

14

반환 값이 필요한 경우 다음이 유용하다는 것을 알았습니다.

result = %x[ls]
puts result

나는 특히 내 컴퓨터에있는 모든 Java 프로세스의 pid를 나열하고 싶었고 이것을 사용했습니다.

ids = %x[ps ax | grep java | awk '{ print $1 }' | xargs]

훌륭한 솔루션입니다.
Ronan Louarn 17 년


9

백틱이나 팝을 사용하는 것이 종종 원하는 것이지만 실제로 실제로 묻는 질문에 대답하지는 않습니다. 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원하는 경우 비슷한 방법으로 캡처 할 수도 있습니다 .


8

을 사용하여 출력을 파일로 리디렉션하려면 다음 Kernel#system과 같이 설명자를 수정할 수 있습니다.

추가 모드에서 stdout 및 stderr을 파일 (/ tmp / log)로 리디렉션하십시오.

system('ls -al', :out => ['/tmp/log', 'a'], :err => ['/tmp/log', 'a'])

장시간 실행되는 명령의 경우 출력이 실시간으로 저장됩니다. IO.pipe를 사용하여 출력을 저장하고 Kernel # system에서 리디렉션 할 수도 있습니다.




당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.