Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

Ruby를 통해 쉘 명령에서 stdout 및 stderr 캡처

tl;dr Ruby에서 셸 명령을 실행하고 stdout, stderr 및 반환 상태를 캡처하려면 Open3.capture3를 확인하세요. 방법. 스트리밍 방식으로 stdout 및 stderr 데이터를 처리하려면 Open3.popen3을 확인하세요. .

너무 많은 나쁜 선택

말 그대로 루비에서 쉘 명령을 실행하는 방법은 492가지가 있으며 각각 약간씩 다르게 작동합니다. 아래 방법 중 하나를 사용했을 것입니다. 내 이동 수단은 항상 백틱(``)이었습니다.

exec("echo 'hello world'") # exits from ruby, then runs the command
system('echo', 'hello world') # returns the status code
sh('echo', 'hello world') # returns the status code
`echo "hello world"` # returns stdout
%x[echo 'hello world'] # returns stdout

그러나 이러한 접근 방식은 매우 제한적입니다. 쉘 명령의 stdout뿐만 아니라 stderr도 캡처해야 한다고 가정합니다. 당신은 단지 운이 좋지 않습니다. 또는 스트림에서 stdout 데이터를 처리하고 명령 실행이 완료되면 한 번에 모두 처리하지 않으려고 가정해 보겠습니다. 운이 없습니다.

또 다른 옵션이 있습니다. 명령을 비동기적으로 실행할 수 있는 기능을 제공하고 stdout, stderr, 종료 코드 및 PID를 제공합니다. 확인해 봅시다!

열기3

이상한 이름의 open3 모듈은 Ruby 표준 라이브러리의 일부입니다. 어떤 역할을 하나요?

Open3는 stdout, stderr, 종료 코드 및 다른 프로그램을 실행할 때 자식 프로세스를 기다리는 스레드에 대한 액세스 권한을 부여합니다. Process.spawn과 동일한 방식으로 프로그램의 다양한 속성, 리디렉션, 현재 디렉터리 등을 지정할 수 있습니다. (_출처:[Open3 Docs](https://ruby-doc.org/stdlib-2.1.0/libdoc/open3/rdoc/Open3.html))_

한 번도 사용하지 않았습니까? 들어본 적도 없나요? 가장 친숙한 라이브러리로 나오지 않기 때문인 것 같습니다. 이름 자체는 Ruby보다 C처럼 들립니다. 그리고 문서는 꽤 하드 코어 목 수염입니다. 하지만 한 번 해보시면 생각보다 그렇게 위협적이지 않다는 것을 알게 되실 것입니다.

캡처3

stdout, stderr 및 상태 코드를 캡처하는 쉬운 방법이 있다면 어떨까요? 글쎄요. 이 기사의 나머지 부분을 읽을 시간이 없다면 capture3이라는 메서드를 사용하여 하루라고 할 수 있다는 점만 알아두세요.

예제를 살펴보겠습니다. 현재 디렉토리에 있는 파일 목록을 가져오려고 한다고 가정합니다. 그렇게 하려면 ls를 실행할 수 있습니다. 명령.

백틱 구문을 사용하는 경우 다음과 같습니다.

puts(`ls`)

capture3 사용 그렇게 보입니다:

require 'open3'
stdout, stderr, status = Open3.capture3("ls")

이것은 명령을 실행하고 stdout 및 stderr을 문자열로 제공합니다. 소란이 없습니다.

보안

일반적으로 사용자에게 웹 서버에서 임의의 명령을 실행할 수 있는 기능을 제공하고 싶지 않습니다. 이것이 identify #{ params[:filename] }와 같은 코드가 정말 끔찍한 생각입니다.

Open3를 사용하면 데이터에서 명령을 분리하여 이와 같은 문제를 피할 수 있습니다. 시스템 방식과 동일하게 작동합니다.

Open3.capture3("identify", params[:filename], other_unsafe_params)

팝픈3

내부적으로 capture3은 popen3이라는 훨씬 더 강력한 방법을 사용합니다. 이 방법은 system()과 같은 친숙한 방법과 약간 다르게 작동합니다.

다음과 같습니다.

require 'open3'
Open3.popen3("ls") do |stdout, stderr, status, thread|
  puts stdout.read
end

파일을 열고 읽을 때와 비슷합니다. 다음과 같은 코드를 본 적이 있을 것입니다.

File.open("my/file/path", "r") do |f|
  puts f.read
end

파이프

Open3에서 stdout 및 stderr은 모두 파일 버퍼처럼 작동하는 파이프입니다. 그리고 파일과 마찬가지로 작업이 끝나면 닫아야 합니다. 이것이 블록 구문의 이유입니다. (비블록 구문이 있지만 stdout 및 stderr에서 수동으로 close를 호출해야 합니다.)

read 메서드는 값을 반환하기 전에 파이프가 닫힐 때까지 기다립니다. 그러나 파이프는 사용 가능하게 되면 판독 라인도 지원합니다. 쉘 명령을 실행하는 데 몇 초가 걸린다고 상상해 보십시오. 그 시간 동안 stderr에 상태 메시지를 인쇄합니다. 이를 캡처하여 사용자에게 표시하고 싶습니다.

한 번에 한 줄씩 stderr을 캡처하는 방법은 다음과 같습니다.

require 'open3'
Open3.popen3("sleep 2; ls") do |stdout, stderr, status, thread|
  while line=stderr.gets do 
    puts(line) 
  end
end

스레드

아직 이야기하지 않은 주장이 하나 있습니다. 스레드입니다.

스레드 인수는 명령이 완료되기를 기다리는 루비 스레드에 대한 참조를 제공합니다. 이제 명령이 스레드에서 실행되지 않습니다. 완전히 별도의 프로세스에서 실행 중입니다. 스레드는 프로세스를 관찰하고 완료될 때까지 기다립니다.

그래도 해당 스레드 참조에서 유용한 데이터를 얻을 수 있습니다.

  • thread.pid - 쉘 명령의 프로세스 ID를 포함합니다. 해당 프로세스에 대해 추가 OS 수준 작업을 수행하려는 경우 이 항목이 필요합니다.

  • thread.status - 프로세스의 종료 상태를 포함합니다. 성공 또는 실패의 경우 1 또는 0입니다.

주의 사항

Open3 문서에서:

교착 상태를 피하기 위해 주의해야 합니다. 파이프는 고정 길이 버퍼이므로 [::popen3](https://ruby-doc.org/stdlib-2.1.0/libdoc/open3/rdoc/Open3.html#method-c-popen3)("prog") {|나, 오, 이, 티| o.read } 프로그램이 stderr에서 너무 많은 출력을 생성하는 경우 교착 상태가 발생합니다. stdout과 stderr을 동시에 읽어야 합니다(스레드 또는 IO.select 사용). 그러나 stderr 출력이 필요하지 않은 경우 [::popen2](https://ruby-doc.org/stdlib-2.1.0/libdoc/open3/rdoc/Open3.html#method-c- 팝픈2). 병합된 stdout과 stderr 출력이 문제가 되지 않는다면 [::popen2e](https://ruby-doc.org/stdlib-2.1.0/libdoc/open3/rdoc/Open3.html#method-c- popen2e). stdout 및 stderr 출력이 별도의 문자열로 정말로 필요한 경우 [::capture3](https://ruby-doc.org/stdlib-2.1.0/libdoc/open3/rdoc/Open3.html#method-c -capture3).