Nice programing

Ruby에서 쉘 명령 호출

nicepro 2020. 9. 27. 13:58
반응형

Ruby에서 쉘 명령 호출


Ruby 프로그램 내부에서 쉘 명령을 어떻게 호출합니까? 그런 다음이 명령의 출력을 Ruby로 다시 가져 오려면 어떻게해야합니까?


이 설명은 내 친구의 주석이 달린 Ruby 스크립트기반으로합니다 . 스크립트를 개선하려면 링크에서 자유롭게 업데이트하십시오.

루비 쉘에게 호출 할 때 첫째, 주, 그것은 일반적으로 호출하는 /bin/sh, 하지 배쉬. 일부 Bash 구문은 /bin/sh모든 시스템에서 지원되지 않습니다 .

쉘 스크립트를 실행하는 방법은 다음과 같습니다.

cmd = "echo 'hi'" # Sample string that can be used
  1. Kernel#` , 일반적으로 백틱이라고합니다. `cmd`

    이것은 Bash, PHP 및 Perl을 포함한 다른 많은 언어와 같습니다.

    쉘 명령의 결과 (즉, 표준 출력)를 반환합니다.

    문서 : http://ruby-doc.org/core/Kernel.html#method-i-60

    value = `echo 'hi'`
    value = `#{cmd}`
    
  2. 내장 구문, %x( cmd )

    x문자 뒤에는 어떤 문자도 될 수있는 구분 기호가 있습니다. 구분 기호는 문자 중 하나 인 경우 (, [, {, 또는 <, 문자 그대로 중첩 구분 쌍을 고려하여, 일치하는 닫는 구분 기호까지의 문자로 구성되어 있습니다. 다른 모든 구분 기호의 경우 리터럴은 구분 기호 문자의 다음 발생까지 문자로 구성됩니다. 문자열 보간 #{ ... }이 허용됩니다.

    백틱처럼 쉘 명령의 결과 (즉, 표준 출력)를 반환합니다.

    문서 : http://www.ruby-doc.org/docs/ProgrammingRuby/html/language.html

    value = %x( echo 'hi' )
    value = %x[ #{cmd} ]
    
  3. Kernel#system

    서브 쉘에서 주어진 명령을 실행합니다.

    true명령이 발견되고 성공적으로 실행되면 반환 하고, false그렇지 않으면 반환합니다.

    문서 : http://ruby-doc.org/core/Kernel.html#method-i-system

    wasGood = system( "echo 'hi'" )
    wasGood = system( cmd )
    
  4. Kernel#exec

    주어진 외부 명령을 실행하여 현재 프로세스를 대체합니다.

    없음을 반환하면 현재 프로세스가 대체되고 계속되지 않습니다.

    문서 : http://ruby-doc.org/core/Kernel.html#method-i-exec

    exec( "echo 'hi'" )
    exec( cmd ) # Note: this will never be reached because of the line above
    

다음은 몇 가지 추가 조언입니다 $?같은 인 $CHILD_STATUS, 당신은 역 따옴표를 사용하는 경우 마지막 시스템 실행 된 명령의 상태를 액세스, system()또는 %x{}. 그런 다음 exitstatuspid속성에 액세스 할 수 있습니다 .

$?.exitstatus

자세한 내용은 다음을 참조하십시오.


이 답변을 기반으로 한 순서도는 다음과 같습니다 . using scriptto emulate a terminal을 참조하십시오 .

여기에 이미지 설명 입력


이 작업을 수행하는 방법은 %x리터럴을 사용하는 것이므로 다음과 같이 명령에서 따옴표를 쉽게 사용할 수 있습니다 (읽을 수 있습니다!).

directorylist = %x[find . -name '*test.rb' | sort]

이 경우 예상대로 처리 할 수있는 현재 디렉터리 아래의 모든 테스트 파일로 파일 목록을 채 웁니다.

directorylist.each do |filename|
  filename.chomp!
  # work with file
end

다음은 Ruby에서 쉘 스크립트를 실행하는 것에 대한 제 생각에 가장 좋은 기사입니다 : " Ruby에서 쉘 명령을 실행하는 6 가지 방법 ".

출력 만 얻으려면 백틱을 사용하십시오.

STDOUT 및 STDERR과 같은 고급 기능이 필요했기 때문에 Open4 gem을 사용했습니다. 거기에 모든 방법이 설명되어 있습니다.


내가 가장 좋아하는 것은 Open3

  require "open3"

  Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }

이러한 메커니즘 중에서 선택할 때 고려해야 할 사항은 다음과 같습니다.

  1. stdout을 원합니까 아니면 stderr도 필요합니까? 또는 분리?
  2. 당신의 출력은 얼마나 큽니까? 전체 결과를 메모리에 저장 하시겠습니까?
  3. 하위 프로세스가 실행되는 동안 일부 출력을 읽으시겠습니까?
  4. 결과 코드가 필요합니까?
  5. 프로세스를 표현하고 필요에 따라 종료 할 수있는 루비 객체가 필요합니까?

간단한 백틱 (``), system () 및 IO.popen본격적인 Kernel.fork/ Kernel.execwith IO.pipeand IO.select.

하위 프로세스를 실행하는 데 너무 오래 걸리는 경우 시간 초과를 혼합에 추가 할 수도 있습니다.

불행하게도, 그것은 매우 많이 의존한다 .


하나 더 옵션 :

때를:

  • stdout뿐만 아니라 stderr이 필요합니다.
  • Open3 / Open4를 사용할 수 없거나 사용할 수 없습니다 (Mac의 NetBeans에서 예외가 발생합니다. 이유를 알 수 없음)

셸 리디렉션을 사용할 수 있습니다.

puts %x[cat bogus.txt].inspect
  => ""

puts %x[cat bogus.txt 2>&1].inspect
  => "cat: bogus.txt: No such file or directory\n"

2>&1구문 은 MS-DOS 초기부터 Linux , Mac 및 Windows 에서 작동합니다 .


저는 확실히 Ruby 전문가는 아니지만 한번 시도해 보겠습니다.

$ irb 
system "echo Hi"
Hi
=> true

또한 다음과 같은 작업을 수행 할 수 있어야합니다.

cmd = 'ls'
system(cmd)

위의 답변은 이미 상당히 훌륭하지만 다음 요약 기사를 공유하고 싶습니다. " Ruby에서 쉘 명령을 실행하는 6 가지 방법 "

기본적으로 다음과 같이 알려줍니다.

Kernel#exec:

exec 'echo "hello $HOSTNAME"'

system$?:

system 'false' 
puts $?

백틱 (`) :

today = `date`

IO#popen:

IO.popen("date") { |f| puts f.gets }

Open3#popen3 -stdlib :

require "open3"
stdin, stdout, stderr = Open3.popen3('dc') 

Open4#popen4 -보석 :

require "open4" 
pid, stdin, stdout, stderr = Open4::popen4 "false" # => [26327, #<IO:0x6dff24>, #<IO:0x6dfee8>, #<IO:0x6dfe84>]

Bash가 정말로 필요한 경우 "최상"답변의 메모에 따라.

루비 쉘에게 호출 할 때 첫째, 주, 그것은 일반적으로 호출하는 /bin/sh, 하지 배쉬. 일부 Bash 구문은 /bin/sh모든 시스템에서 지원되지 않습니다 .

Bash를 사용해야하는 경우 bash -c "your Bash-only command"원하는 호출 메서드 내부에 삽입 합니다.

quick_output = system("ls -la")

quick_bash = system("bash -c 'ls -la'")

테스트하려면 :

system("echo $SHELL") system('bash -c "echo $SHELL"')

또는 기존 스크립트 파일 (예 :)을 실행하는 경우 script_output = system("./my_script.sh")Ruby shebang을 존중 해야 하지만 항상을 사용 system("bash ./my_script.sh")하여 확인할 수 있습니다 ( /bin/sh실행시 약간의 오버 헤드가있을 수 있지만 /bin/bash눈치 채지 못할 것입니다.


Perl과 유사한 백틱 연산자 (`)를 사용할 수도 있습니다.

directoryListing = `ls /`
puts directoryListing # prints the contents of the root directory

간단한 것이 필요한 경우 편리합니다.

사용하려는 방법은 수행하려는 작업에 따라 다릅니다. 다른 방법에 대한 자세한 내용은 문서를 확인하십시오.


가장 쉬운 방법은 다음과 같습니다.

reboot = `init 6`
puts reboot

여기에 답변을 사용하고 Mihai의 답변에 링크 된 것을 사용하여 다음 요구 사항을 충족하는 기능을 모았습니다.

  1. 내 스크립트가 콘솔에서 실행될 때 "누출"되지 않도록 STDOUT 및 STDERR을 깔끔하게 캡처합니다.
  2. 인수가 배열로 셸에 전달되도록 허용하므로 이스케이프에 대해 걱정할 필요가 없습니다.
  3. 명령의 종료 상태를 캡처하여 오류가 발생했을 때 명확하게합니다.

보너스로 이것은 쉘 명령이 성공적으로 종료되고 (0) STDOUT에 아무것도 넣는 경우에도 STDOUT을 반환합니다. 이러한 방식으로 는 이러한 경우에 system단순히 반환 되는와 다릅니다 true.

코드는 다음과 같습니다. 구체적인 기능은 system_quietly다음과 같습니다.

require 'open3'

class ShellError < StandardError; end

#actual function:
def system_quietly(*cmd)
  exit_status=nil
  err=nil
  out=nil
  Open3.popen3(*cmd) do |stdin, stdout, stderr, wait_thread|
    err = stderr.gets(nil)
    out = stdout.gets(nil)
    [stdin, stdout, stderr].each{|stream| stream.send('close')}
    exit_status = wait_thread.value
  end
  if exit_status.to_i > 0
    err = err.chomp if err
    raise ShellError, err
  elsif out
    return out.chomp
  else
    return true
  end
end

#calling it:
begin
  puts system_quietly('which', 'ruby')
rescue ShellError
  abort "Looks like you don't have the `ruby` command. Odd."
end

#output: => "/Users/me/.rvm/rubies/ruby-1.9.2-p136/bin/ruby"

우리는 여러 가지 방법으로 그것을 달성 할 수 있습니다.

를 사용하면 Kernel#exec이 명령이 실행 된 후에는 아무것도 없습니다.

exec('ls ~')

사용 backticks or %x

`ls ~`
=> "Applications\nDesktop\nDocuments"
%x(ls ~)
=> "Applications\nDesktop\nDocuments"

Using Kernel#system command, returns true if successful, false if unsuccessful and returns nil if command execution fails:

system('ls ~')
=> true

Don't forget the spawn command to create a background process to execute the specified command. You can even wait for its completion using the Process class and the returned pid:

pid = spawn("tar xf ruby-2.0.0-p195.tar.bz2")
Process.wait pid

pid = spawn(RbConfig.ruby, "-eputs'Hello, world!'")
Process.wait pid

The doc says: This method is similar to #system but it doesn't wait for the command to finish.


If you have a more complex case than the common case (that can not be handled with ``) then check out Kernel.spawn() here. This seems to be the most generic/full-featured provided by stock Ruby to execute external commands.

E.g. you can use it to:

  • create process groups (Windows)
  • redirect in, out, error to files/each-other.
  • set env vars, umask
  • change dir before executing command
  • set resource limits for CPU/data/...
  • Do everything that can be done with other options in other answers, but with more code.

Official ruby documentation has good enough examples.

env: hash
  name => val : set the environment variable
  name => nil : unset the environment variable
command...:
  commandline                 : command line string which is passed to the standard shell
  cmdname, arg1, ...          : command name and one or more arguments (no shell)
  [cmdname, argv0], arg1, ... : command name, argv[0] and zero or more arguments (no shell)
options: hash
  clearing environment variables:
    :unsetenv_others => true   : clear environment variables except specified by env
    :unsetenv_others => false  : dont clear (default)
  process group:
    :pgroup => true or 0 : make a new process group
    :pgroup => pgid      : join to specified process group
    :pgroup => nil       : dont change the process group (default)
  create new process group: Windows only
    :new_pgroup => true  : the new process is the root process of a new process group
    :new_pgroup => false : dont create a new process group (default)
  resource limit: resourcename is core, cpu, data, etc.  See Process.setrlimit.
    :rlimit_resourcename => limit
    :rlimit_resourcename => [cur_limit, max_limit]
  current directory:
    :chdir => str
  umask:
    :umask => int
  redirection:
    key:
      FD              : single file descriptor in child process
      [FD, FD, ...]   : multiple file descriptor in child process
    value:
      FD                        : redirect to the file descriptor in parent process
      string                    : redirect to file with open(string, "r" or "w")
      [string]                  : redirect to file with open(string, File::RDONLY)
      [string, open_mode]       : redirect to file with open(string, open_mode, 0644)
      [string, open_mode, perm] : redirect to file with open(string, open_mode, perm)
      [:child, FD]              : redirect to the redirected file descriptor
      :close                    : close the file descriptor in child process
    FD is one of follows
      :in     : the file descriptor 0 which is the standard input
      :out    : the file descriptor 1 which is the standard output
      :err    : the file descriptor 2 which is the standard error
      integer : the file descriptor of specified the integer
      io      : the file descriptor specified as io.fileno
  file descriptor inheritance: close non-redirected non-standard fds (3, 4, 5, ...) or not
    :close_others => false : inherit fds (default for system and exec)
    :close_others => true  : dont inherit (default for spawn and IO.popen)

  • backticks ` method is the easiest one to call shell commands from ruby. It returns the result of the shell command.

     url_request = 'http://google.com'
     result_of_shell_command = `curl #{url_request}`
    

Given a command eg attrib

require 'open3'

a="attrib"
Open3.popen3(a) do |stdin, stdout, stderr|
  puts stdout.read
end

I've found that while this method isn't as memorable as e.g. system("thecommand") or thecommand in backticks, a good thing about this method compared to other methods.. is e.g. backticks doesn't seem to let me 'puts' the command I run / store the command I want to run in a variable, and system("thecommand") doesn't seem to let me get the output. Whereas this method lets me do both of those things, and it lets me access stdin, stdout and stderr independently.

https://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html

http://ruby-doc.org/stdlib-2.4.1/libdoc/open3/rdoc/Open3.html


Not really an answer but maybe someone will find this useful, and its regarding to this.

Windows에서 TK GUI를 사용할 때 rubyw에서 쉘 명령을 호출해야 할 때, u는 항상 성가신 cmd 창이 1 초 미만 동안 팝업됩니다.

이것을 피하려면 사용할 수 있습니다

WIN32OLE.new('Shell.Application').ShellExecute('ipconfig > log.txt','','','open',0)

또는

WIN32OLE.new('WScript.Shell').Run('ipconfig > log.txt',0,0)

둘 다 ipconfig의 출력을 'log.txt'에 저장하지만 창이 나타나지 않습니다.

U는 require 'win32ole'스크립트 내부에 필요합니다 .

system(), exec()그리고 spawn()TK와 rubyw를 사용할 때 모두 성가신 창을 띄울 것입니다.


다음은 OS X의 루비 스크립트에서 사용하는 멋진 것입니다 (스크립트를 시작하고 창에서 전환 한 후에도 업데이트를받을 수 있습니다).

cmd = %Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )

참고 URL : https://stackoverflow.com/questions/2232/calling-shell-commands-from-ruby

반응형