การรันคำสั่งบรรทัดคำสั่งภายในสคริปต์ Ruby


92

มีวิธีเรียกใช้คำสั่งบรรทัดคำสั่งผ่าน Ruby หรือไม่? ฉันกำลังพยายามสร้างโปรแกรม Ruby เล็ก ๆ ที่สามารถโทรออกและรับ / ส่งผ่านโปรแกรมบรรทัดคำสั่งเช่น 'screen', 'rcsz' เป็นต้น

จะดีมากถ้าฉันสามารถผูกทั้งหมดนี้เข้ากับ Ruby (แบ็กเอนด์ MySQL ฯลฯ )


อาจซ้ำกันของการเรียกคำสั่งเชลล์จาก Ruby
ymoreau

คำตอบ:


210

ใช่. มีหลายวิธี:


ก. ใช้%xหรือ '' ':

%x(echo hi) #=> "hi\n"
%x(echo hi >&2) #=> "" (prints 'hi' to stderr)

`echo hi` #=> "hi\n"
`echo hi >&2` #=> "" (prints 'hi' to stderr)

วิธีการเหล่านี้จะคืนค่า stdout และเปลี่ยนเส้นทาง stderr ไปยังโปรแกรม


ข. ใช้system:

system 'echo hi' #=> true (prints 'hi')
system 'echo hi >&2' #=> true (prints 'hi' to stderr)
system 'exit 1' #=> nil

วิธีนี้จะส่งคืนtrueหากคำสั่งสำเร็จ มันเปลี่ยนเส้นทางผลลัพธ์ทั้งหมดไปยังโปรแกรม


ค. ใช้exec:

fork { exec 'sleep 60' } # you see a new process in top, "sleep", but no extra ruby process. 
exec 'echo hi' # prints 'hi'
# the code will never get here.

ซึ่งจะแทนที่กระบวนการปัจจุบันด้วยกระบวนการที่สร้างขึ้นโดยคำสั่ง


ง. (ทับทิม 1.9) ใช้spawn:

spawn 'sleep 1; echo one' #=> 430
spawn 'echo two' #=> 431
sleep 2
# This program will print "two\none".

วิธีนี้ไม่รอให้กระบวนการออกและส่งคืน PID


จ. ใช้IO.popen:

io = IO.popen 'cat', 'r+'
$stdout = io
puts 'hi'
$stdout = IO.new 0
p io.read(1)
io.close
# prints '"h"'.

วิธีนี้จะส่งคืนIOวัตถุที่แสดงอินพุต / เอาต์พุตของกระบวนการใหม่ซ้ำ ขณะนี้ยังเป็นวิธีเดียวที่ฉันรู้ในการป้อนข้อมูลโปรแกรม


ฉ. ใช้Open3(บน 1.9.2 ขึ้นไป)

require 'open3'

stdout,stderr,status = Open3.capture3(some_command)
STDERR.puts stderr
if status.successful?
  puts stdout
else
  STDERR.puts "OH NO!"
end

Open3มีฟังก์ชันอื่น ๆ อีกมากมายสำหรับการเข้าถึงอย่างชัดเจนไปยังเอาต์พุตสตรีมทั้งสอง คล้ายกับ popen แต่ให้คุณเข้าถึง stderr


เคล็ดลับโบนัส: io = IO.popen 'cat > out.log', 'r+'; เขียนผลลัพธ์ของคำสั่งเป็น "out.log"
Narfanator

1
อะไรคือข้อดีและข้อเสียสำหรับแต่ละรายการ ฉันจะตัดสินใจได้อย่างไรว่าจะใช้ตัวไหน? วิธีการเกี่ยวกับการใช้FileUtils[ ruby-doc.org/stdlib-1.9.3/libdoc/fileutils/rdoc/FileUtils.html] ?
อวา

1
ฉันกำลังดำเนินการคำสั่ง SVN โดยใช้ exec ฉันไม่ต้องการให้ผลลัพธ์ของ exec ปรากฏบนคอนโซล ฉันต้องการเปลี่ยนเส้นทางเพื่อที่ฉันจะได้เก็บไว้เป็นตัวแปรและดำเนินการกับสิ่งนี้ ฉันต้องทำอย่างไร ?
stack1

2
สถานะสำเร็จ? ไม่ทำงานให้ฉันใน Ruby 2.4 อีกต่อไปมันเปลี่ยนเป็นสถานะสำเร็จ? :)
DanielG

14

มีสองสามวิธีในการเรียกใช้คำสั่งระบบใน Ruby

irb(main):003:0> `date /t` # surround with backticks
=> "Thu 07/01/2010 \n"
irb(main):004:0> system("date /t") # system command (returns true/false)
Thu 07/01/2010
=> true
irb(main):005:0> %x{date /t} # %x{} wrapper
=> "Thu 07/01/2010 \n"

แต่ถ้าคุณจำเป็นต้องดำเนินการอินพุตและเอาต์พุตด้วย stdin / stdout ของคำสั่งคุณอาจต้องการดูIO::popenวิธีการซึ่งนำเสนอสิ่งอำนวยความสะดวกนั้นโดยเฉพาะ


popen ทำงานได้ดีหากแอปของคุณมีมาตรฐาน หากคุณต้องการการโต้ตอบเพิ่มเติมหรือต้องการทำสิ่งที่แตกต่างกับ stdout, stdin และโดยเฉพาะอย่างยิ่ง stderr คุณจะต้องดู open3: ruby-doc.org/core/classes/Open3.html
Paul Rubel


2

ใช่สิ่งนี้ทำได้อย่างแน่นอน แต่วิธีการใช้งานจะแตกต่างกันไปขึ้นอยู่กับว่าโปรแกรม "บรรทัดคำสั่ง" ที่เป็นปัญหาทำงานในโหมด "เต็มหน้าจอ" หรือโหมดบรรทัดคำสั่ง โปรแกรมที่เขียนสำหรับบรรทัดคำสั่งมักจะอ่าน STDIN และเขียนถึง STDOUT สิ่งเหล่านี้สามารถเรียกได้โดยตรงภายใน Ruby โดยใช้เมธอด backticks มาตรฐานและ / หรือการเรียกระบบ / exec

หากโปรแกรมทำงานในโหมด "เต็มหน้าจอ" เช่นหน้าจอหรือ vi วิธีการนั้นจะต้องแตกต่างออกไป สำหรับโปรแกรมเช่นนี้คุณควรมองหาการใช้งาน Ruby ของไลบรารี "คาดหวัง" สิ่งนี้จะช่วยให้คุณสามารถเขียนสคริปต์สิ่งที่คุณคาดว่าจะเห็นบนหน้าจอและสิ่งที่จะส่งเมื่อคุณเห็นสตริงเหล่านั้นปรากฏบนหน้าจอ

นี่ไม่น่าจะเป็นแนวทางที่ดีที่สุดและคุณน่าจะดูสิ่งที่คุณพยายามจะบรรลุและค้นหาไลบรารี / อัญมณีที่เกี่ยวข้องเพื่อทำสิ่งนั้นแทนที่จะพยายามทำให้แอปพลิเคชันเต็มหน้าจอที่มีอยู่เป็นไปโดยอัตโนมัติ ดังตัวอย่าง " ต้องการความช่วยเหลือเกี่ยวกับการสื่อสารพอร์ตอนุกรมใน Ruby " ที่เกี่ยวข้องกับการสื่อสาร Serial Port ซึ่งเป็นเคอร์เซอร์ล่วงหน้าเพื่อโทรออกหากนั่นคือสิ่งที่คุณต้องการบรรลุโดยใช้โปรแกรมเฉพาะที่คุณกล่าวถึง


Expect เวอร์ชันที่เรียบง่ายมีอยู่ใน Ruby โดยใช้โมดูลPtyในตัว
คนดีบุก

0

วิธีที่ใช้บ่อยที่สุดคือการใช้Open3ที่นี่คือโค้ดของฉันเวอร์ชันแก้ไขโค้ดด้านบนพร้อมการแก้ไขบางส่วน:

require 'open3'
puts"Enter the command for execution"
some_command=gets
stdout,stderr,status = Open3.capture3(some_command)
STDERR.puts stderr
if status.success?
  puts stdout
else
  STDERR.puts "ERRRR"
end
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.