Pertanyaan Memanggil perintah shell dari Ruby


Bagaimana cara memanggil perintah shell dari dalam program Ruby? Bagaimana saya kemudian mendapatkan output dari perintah-perintah ini kembali ke Ruby?


883
2017-08-05 12:56


asal


Jawaban:


Penjelasan ini didasarkan pada komentar Skrip Ruby dari seorang teman saya. Jika Anda ingin meningkatkan skrip, jangan ragu untuk memperbaruinya di tautan.

Pertama, perhatikan bahwa ketika Ruby memanggil ke shell, biasanya panggilan /bin/sh, tidak Pesta. Beberapa sintaks Bash tidak didukung oleh /bin/sh pada semua sistem.

Berikut ini cara untuk menjalankan skrip shell:

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

    Ini seperti banyak bahasa lainnya, termasuk Bash, PHP, dan Perl.

    Mengembalikan hasil dari perintah shell.

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

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

    Mengikuti x karakter adalah pemisah, yang dapat berupa karakter apa pun. Jika pemisah adalah salah satu karakter (, [, {, atau <, literal terdiri dari karakter hingga pembatas penutup yang cocok, memperhitungkan pasangan pembatas bersarang. Untuk semua pembatas lainnya, literal terdiri dari karakter hingga kemunculan berikutnya dari karakter pembatas. Interpolasi string #{ ... } Diperbolehkan.

    Mengembalikan hasil perintah shell, seperti backticks.

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

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

    Menjalankan perintah yang diberikan dalam subkulit.

    Pengembalian true jika perintah itu ditemukan dan berhasil dijalankan, false jika tidak.

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

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

    Menggantikan proses saat ini dengan menjalankan perintah eksternal yang diberikan.

    Tidak ada yang dikembalikan, proses saat ini diganti dan tidak pernah berlanjut.

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

Berikut beberapa saran tambahan: $?, yang sama dengan $CHILD_STATUS, mengakses status perintah sistem terakhir yang dieksekusi jika Anda menggunakan backticks, system() atau %x{}. Anda kemudian dapat mengakses exitstatus dan pid properti:

$?.exitstatus

Untuk bacaan selengkapnya, lihat:


1158
2017-08-05 14:42



Cara saya suka melakukan ini adalah menggunakan %x literal, yang membuatnya mudah (dan mudah dibaca!) untuk menggunakan tanda kutip dalam sebuah perintah, seperti:

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

Yang, dalam hal ini, akan mengisi daftar file dengan semua file uji di bawah direktori saat ini, yang dapat Anda proses sesuai harapan:

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

151
2017-08-05 14:08



Berikut adalah diagram alur berdasarkan jawaban ini. Lihat juga, menggunakan script untuk meniru terminal.

enter image description here


148
2018-05-19 17:01



Berikut artikel terbaik menurut saya tentang menjalankan skrip shell di Ruby: "6 Cara Menjalankan Perintah Shell di Ruby".

Jika Anda hanya perlu mendapatkan output, gunakan backticks.

Saya membutuhkan hal-hal yang lebih canggih seperti STDOUT dan STDERR jadi saya menggunakan permata Open4. Anda memiliki semua metode yang dijelaskan di sana.


58
2017-09-02 11:05



Favorit saya adalah Open3

  require "open3"

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

31
2017-09-18 17:47



Beberapa hal yang perlu dipikirkan saat memilih di antara mekanisme ini adalah:

  1. Apakah Anda hanya ingin stdout atau apakah Anda perlu stderr juga? atau bahkan dipisahkan?
  2. Seberapa besar output Anda? Apa kau mau untuk menahan seluruh hasil dalam ingatan?
  3. Apakah Anda ingin membaca beberapa dari Anda output sementara subprocess masih berlari?
  4. Apakah Anda perlu kode hasil?
  5. Apakah Anda membutuhkan objek ruby ​​itu mewakili proses dan memungkinkan Anda membunuhnya jika diminta?

Anda mungkin memerlukan apa pun dari backticks sederhana (``), sistem (), dan IO.popen menjadi besar Kernel.fork/Kernel.exec dengan IO.pipe dan IO.select.

Anda mungkin juga ingin membuang waktu ke dalam mix jika proses subproses terlalu lama untuk dieksekusi.

Sayangnya, sangat banyak tergantung.


23
2017-08-07 05:10



Satu opsi lagi:

Ketika Anda:

  • perlu stderr serta stdout
  • tidak dapat / tidak akan menggunakan Open3 / Open4 (mereka melempar pengecualian di NetBeans pada Mac saya, tidak tahu mengapa)

Anda dapat menggunakan pengalihan shell:

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

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

Itu 2>&1 sintaks bekerja Linux, Mac, dan Windows sejak hari-hari awal MS-DOS.


19
2018-06-16 02:13