Ringer skallkommandoer fra Ruby

stemmer
899

Hvordan kan jeg kalle shell-kommandoer fra innsiden av et Ruby program? Hvordan kan jeg da få utgang fra disse kommandoene tilbake til Ruby?

Publisert på 05/08/2008 klokken 12:56
kilden bruker
På andre språk...                            


20 svar

stemmer
1k

Denne forklaringen er basert på en kommen Ruby skript fra en venn av meg. Hvis du ønsker å forbedre manuset, gjerne oppdatere den på linken.

Først oppmerksom på at når Ruby roper til et skall, det vanligvis kaller /bin/sh, ikke Bash. Noen Bash syntaks støttes ikke av /bin/shpå alle systemer.

Her er måter å utføre et shell script:

cmd = "echo 'hi'" # Sample string that can be used
  1. Kernel#` , Ofte kalt backticks - `cmd`

    Dette er som mange andre språk, inkludert Bash, PHP og Perl.

    Returnerer resultatet av skallet kommandoen.

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

    value = `echo 'hi'`
    value = `#{cmd}`
    
  2. Innebygd syntaks, %x( cmd )

    Etter xkarakter er et skilletegn som kan være hvilket som helst tegn. Hvis skille er en av figurene (, [, {, eller <, den bokstavelige består av tegnene opp til den passende lukkende skilletegn, idet det tas hensyn nestede avgrensingspar. For alle andre skilletegn omfatter tegnene frem til den neste forekomst av skilletegn. String interpolering #{ ... }er tillatt.

    Returnerer resultatet av skallet kommando, akkurat som backticks.

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

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

    Utfører en gitt kommando i et shell.

    Returnerer truehvis kommandoen ble funnet og var vellykket, falseellers.

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

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

    Erstatter den nåværende prosessen ved å kjøre en gitt ekstern kommando.

    Returnerer ingen, den nåværende prosessen er erstattet og aldri fortsetter.

    Dokumenter: 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
    

Her er noen ekstra råd: $?, som er det samme som $CHILD_STATUS, åpner status for den siste system utført kommando hvis du bruker backticks, system()eller %x{}. Du kan deretter få tilgang til exitstatusog pidegenskaper:

$?.exitstatus

For mer lesing, se:

Svarte 05/08/2008 kl. 14:42
kilden bruker

stemmer
161

Her er et flytdiagram basert på dette svaret . Se også, ved hjelp av scriptå etterligne en terminal .

skriv bildebeskrivelse her

Svarte 19/05/2016 kl. 17:01
kilden bruker

stemmer
153

Slik jeg liker å gjøre dette på er å bruke %xbokstavelig, noe som gjør det enkelt å bruke sitater i en kommando, som så (og lesbar!):

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

Som i dette tilfellet vil fylle fil liste med alle testfiler under den gjeldende katalogen, som du kan behandle som forventet:

directorylist.each do |filename|
  filename.chomp!
  # work with file
end
Svarte 05/08/2008 kl. 14:08
kilden bruker

stemmer
59

Her er den beste artikkel i min mening om å kjøre skallskript i Ruby: " 6 måter å kjøre skallkommandoer i Ruby ".

Hvis du bare trenger å få utgang bruk backticks.

Jeg trengte mer avanserte ting som STDOUT og STDERR så jeg brukte Open4 perle. Du har alle metodene forklart der.

Svarte 02/09/2008 kl. 11:05
kilden bruker

stemmer
32

Min favoritt er Open3

  require "open3"

  Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }
Svarte 18/09/2008 kl. 17:47
kilden bruker

stemmer
23

Noen ting å tenke på når du velger mellom disse mekanismene er:

  1. Har du bare ønsker stdout eller trenger du stderr også? eller til og med separerte ut?
  2. Hvor stor er produksjonen din? Ønsker du å holde hele resultatet i minnet?
  3. Ønsker du å lese noen av kopiene mens subprosessen fortsatt kjører?
  4. Trenger du resultatkoder?
  5. Trenger du en rubin objekt som representerer prosessen og lar deg drepe den ved behov?

Du må kanskje alt fra enkle backticks ( ``), system (), og IO.popentil full-blåst Kernel.fork/ Kernel.execmed IO.pipeog IO.select.

Du kan også være lurt å kaste tidsavbrudd inn i blandingen hvis en delprosess tar for lang tid å gjennomføre.

Dessverre er det veldig mye avhenger .

Svarte 07/08/2008 kl. 05:10
kilden bruker

stemmer
20

En mer alternativ:

Når du:

  • trenger stderr samt stdout
  • kan ikke / vil ikke bruke Open3 / Open4 (de kaster unntak i NetBeans på min Mac, aner ikke hvorfor)

Du kan bruke shell omdirigering:

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

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

Den 2>&1syntaks fungerer på tvers av Linux , Mac og Windows siden de tidlige dager av MS-DOS.

Svarte 16/06/2010 kl. 02:13
kilden bruker

stemmer
16

Jeg er definitivt ikke en Ruby ekspert, men jeg skal gi det en sjanse:

$ irb 
system "echo Hi"
Hi
=> true

Du bør også være i stand til å gjøre ting som:

cmd = 'ls'
system(cmd)
Svarte 05/08/2008 kl. 13:24
kilden bruker

stemmer
12

Svarene ovenfor er allerede ganske stor, men jeg virkelig ønsker å dele følgende oppsummering artikkel: " 6 måter å kjøre skallkommandoer i Ruby "

I utgangspunktet, forteller det oss:

Kernel#exec:

exec 'echo "hello $HOSTNAME"'

systemog $?:

system 'false' 
puts $?

Backticks ( '):

today = `date`

IO#popen:

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

Open3#popen3 - stdlib:

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

Open4#popen4 - en perle:

require "open4" 
pid, stdin, stdout, stderr = Open4::popen4 "false" # => [26327, #<IO:0x6dff24>, #<IO:0x6dfee8>, #<IO:0x6dfe84>]
Svarte 07/06/2013 kl. 02:07
kilden bruker

stemmer
9

Hvis du virkelig trenger Bash, per notatet i den "beste" svaret.

Først oppmerksom på at når Ruby roper til et skall, det vanligvis kaller /bin/sh, ikke Bash. Noen Bash syntaks støttes ikke av /bin/shpå alle systemer.

Hvis du trenger å bruke Bash, sett bash -c "your Bash-only command"innsiden av ønsket å kalle metoden.

quick_output = system("ls -la")

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

Å teste:

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

Eller hvis du kjører et eksisterende skript fil (for eksempel script_output = system("./my_script.sh")) Ruby skal hedre shebang, men du kan alltid bruke system("bash ./my_script.sh")til å sørge for at (selv om det kan være en liten overhead fra /bin/shå kjøre /bin/bash, har du sannsynligvis ikke vil merke.

Svarte 02/06/2017 kl. 20:14
kilden bruker

stemmer
7

Du kan også bruke bakover-apostrof operatører ( `), i likhet med Perl:

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

Nyttig hvis du trenger noe enkelt.

Hvilken metode du vil bruke, avhenger av hva du prøver å oppnå; sjekk docs for mer informasjon om de ulike metodene.

Svarte 05/08/2008 kl. 13:57
kilden bruker

stemmer
5

enkleste måten er, for eksempel:

reboot = `init 6`
puts reboot
Svarte 30/03/2017 kl. 18:13
kilden bruker

stemmer
5

Ikke glem spawnkommando for å opprette en bakgrunnsprosess for å utføre den angitte kommandoen. Du kan også vente til den er ferdig med Processklasse og tilbake 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

Doc sier: Denne metoden er lik #system, men det trenger ikke vente på kommando til slutt.

Svarte 04/11/2015 kl. 15:04
kilden bruker

stemmer
5

Ved hjelp av svarene her og knyttet i Mihai svar, jeg satt sammen en funksjon som oppfyller disse kravene:

  1. Pent fanger STDOUT og STDERR slik at de ikke "lekker" når min skriptet er kjørt fra konsollen.
  2. Lar argumenter som skal sendes til skallet som en matrise, så det er ingen grunn til å bekymre rømme.
  3. Fanger exit status av kommandoen så det er klart når det har oppstått en feil.

Som en bonus, vil dette også returnere STDOUT i tilfeller hvor skall-kommandoen avslutter vellykket (0) og setter noe på STDOUT. På denne måten skiller det fra system, som rett og slett er tilbake truei slike tilfeller.

Kode følger. Den spesifikke funksjon er 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"
Svarte 20/02/2012 kl. 23:36
kilden bruker

stemmer
5

Vi kan oppnå det på flere måter.

Ved hjelp Kernel#exec, ingenting etter denne kommandoen er utført:

exec('ls ~')

Ved hjelp av backticks or %x

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

Ved hjelp av Kernel#systemkommandoen, returnerer truehvis det lykkes, falsehvis mislykket og går tilbake nilhvis kommandoen kjøres mislykkes:

system('ls ~')
=> true
Svarte 19/02/2012 kl. 18:07
kilden bruker

stemmer
3
  • backticks `metode er den enkleste å ringe skallkommandoer fra rubin. Den returnerer resultatet av skallkommando.

     url_request = 'http://google.com'
     result_of_shell_command = `curl #{url_request}`
    
Svarte 16/02/2017 kl. 09:58
kilden bruker

stemmer
3

Hvis du har en mer komplisert sak enn den vanlige tilfelle (som ikke kan håndteres med ``) så sjekk ut Kernel.spawn() her . Dette synes å være den mest generiske / fullverdig levert av lager Ruby å utføre eksterne kommandoer.

F.eks du kan bruke den til:

  • skape prosess grupper (Windows)
  • omdirigere inn, ut, feil på filer / each-other.
  • sett env vars, umask
  • endre dir før man utfører kommandoen
  • sett ressursgrenser for CPU / data / ...
  • Gjør alt som kan gjøres med andre alternativer i andre svar, men med mer kode.

Offisiell rubin dokumentasjon har gode nok eksempler.

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)
Svarte 11/12/2015 kl. 14:57
kilden bruker

stemmer
1

Gitt en kommando f.eks attrib

require 'open3'

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

Jeg har funnet ut at mens denne metoden er ikke like minneverdig som for eksempel system ( "thecommand") eller thecommand i backticks, en god ting om denne metoden i forhold til andre metoder .. er f.eks backticks ser ikke ut til å la meg 'puts 'kommando jeg kjøre / store kommandoen jeg ønsker å kjøre i en variabel, og systemet ( 'thecommand') ser ikke ut til å la meg få produksjonen. Mens denne metoden lar meg gjøre begge disse tingene, og det lar meg tilgang stdin, stdout og stderr uavhengig.

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

Svarte 19/12/2017 kl. 05:54
kilden bruker

stemmer
0

Ikke egentlig et svar, men kanskje noen vil finne dette nyttig, og det er om å dette.

Ved bruk av TK GUI på Windows, og u må ringe shell-kommandoer fra rubyw, vil u alltid har en irriterende cmd vindu dukker opp for mindre enn et sekund.

For å unngå dette u kan bruke

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

eller

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

Begge vil lagre ipconfig utgang innsiden 'log.txt', men ingen vinduer vil komme opp.

U må require 'win32ole'i skriptet.

system(), exec()Og spawn()vil alle dukke opp som irriterende vinduet når du bruker TK og rubyw.

Svarte 05/07/2018 kl. 12:55
kilden bruker

stemmer
-1

Her er en kul en som jeg bruker i en ruby ​​skript på OS X (slik at jeg kan starte et script og få en oppdatering selv etter veksling bort fra vinduet):

cmd = %Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )
Svarte 14/10/2014 kl. 20:12
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more