หากคุณต้องการรันคำสั่งภายนอกจาก Ruby...
...เช่น wkhtmltopdf เพื่อแปลงไฟล์ HTML เป็น PDF
มีเมธอด Ruby จำนวนน้อยที่คุณใช้ได้
ขึ้นอยู่กับเมธอดที่คุณใช้ คุณจะได้ผลลัพธ์ที่ต่างกัน
มา探索เมธอดเหล่านี้กัน!
เมธอด System ของ Ruby
เมธอด system ของ Ruby เป็นวิธีง่ายที่สุดในการรันคำสั่งภายนอก
มันเป็นแบบนี้:
system("ls")
สังเกตว่า system จะพิมพ์เอาต์พุทของคำสั่งขณะเกิดขึ้น
นอกจากนี้ system จะทำให้โปรแกรม Ruby ของคุณ รอจนกว่าคำสั่งจะเสร็จ
ลองนี้:
system("sleep 2")
มีวิธีรันคำสั่งในเบื้องหลัง (background) ตามที่เราจะเห็น
System สามารถคืนค่า 3 ค่าที่เป็นไปได้:
trueหากคำสั่งทำงานสำเร็จfalseหากคำสั่งคืนรหัสข้อผิดพลาดnilหากการรันคำสั่งล้มเหลว (command not found)
คุณสามารถรับ exit status code ของคำสั่งภายนอกล่าสุดที่คุณรัน ด้วยตัวแปร global $? รหัสสถานะนี้ให้ข้อมูลเพิ่มเติมว่าทำไมคำสั่งถึงล้มเหลว
Shell Expansion คืออะไร?
เมื่อคุณรันคำสั่งภายนอกด้วย system คุณจะได้เอฟเฟกต์ "shell expansion"
Shell expansion คือเมื่อ shell (sh/bash/zsh) นำอักขระพิเศษ (*, ?, และ [) จากคำสั่งของคุณ และ ขยายพวกมันเป็นรายการไฟล์
ตัวอย่าง:
system("echo *")
สิ่งนี้จะพิมพ์ทุกไฟล์และโฟลเดอร์ในไดเรกทอรี่ปัจจุบัน
* ถูกแทนที่ด้วยรายการไฟล์ก่อนที่คำสั่งจะรัน
หากคุณต้องการใช้อักขระพิเศษเหล่านี้เป็นต้นฉบับ ส่งอาร์กิวเมนต์เป็นอาร์กิวเมนต์ตัวที่ 2 ของ system
เช่นนี้:
system("echo", "*")
ตอนนี้เอาต์พุทจะเป็น *
วิธีใช้ Environment Variables
หากคุณต้องการส่งตัวแปรสภาพแวดล้อมเฉพาะไปยังคำสั่งภายนอก คุณสามารถใช้ hash เป็นอาร์กิวเมนต์ตัวแรก
ตัวอย่าง:
system({"rubyguides" => "best"}, "ruby", "-e p ENV['rubyguides']")
# "best"
สิ่งนี้จะไม่เปลี่ยนสภาพแวดล้อมปัจจุบันของแอป Ruby ของคุณ
%x / Kernel#`
หากคุณต้องการรับ output จากคำสั่งที่คุณรัน แทนที่จะแสดงผล คุณสามารถใช้ %x หรือเมธอด Kernel#`
พวกมันทำสิ่งเดียวกัน
ตัวอย่าง:
`ls`
อีกตัวอย่างกับ %x:
%x|ls|
ทั้งสองตัวอย่างนี้จะคืนค่า สตริงพร้อมเอาต์พุท ของคำสั่ง ls
สังเกตว่าคุณยังต้องรอจนกว่าคำสั่งจะเสร็จ เว้นแต่คุณรันมันใน thread
วิธีใช้ Fork + Exec เพื่อรันคำสั่งภายนอกบน Process แยก
Forking ทำสำเนาของกระบวนการปัจจุบัน (แอป Ruby ของคุณ) แล้วคุณสามารถ แทนที่สำเนานั้นด้วยกระบวนการอื่นโดยใช้ exec
หมายเหตุ: เมธอด fork ไม่มีใน Windows
นี่เป็นรูปแบบทั่วไปในโลก Unix
วิธีทำงาน:
fork { exec("ls") }
สิ่งนี้จะรัน ls บนกระบวนการอื่น และแสดงเอาต์พุท
เพราะ คำสั่งนี้รันบนกระบวนการอื่น มันจะไม่บล็อกแอป Ruby ของคุณจากการรัน เช่นเมธอด system หรือ %x
สำคัญ:
หากคุณใช้ exec โดยไม่มี fork คุณจะแทนที่กระบวนการปัจจุบัน
หมายความว่าโปรแกรม Ruby ของคุณจะจบ
วิธีใช้เมธอด Popen สำหรับการสื่อสารสองทางกับโปรแกรมภายนอก
หากคุณต้องการ:
- ควบคุมกระบวนการมากขึ้น
- การสื่อสารสองทาง (two-way communication)
เมธอด IO.popen คือสิ่งที่คุณหา
ในตัวอย่างต่อไปนี้ ผมใช้ popen method เพื่อเปิดกระบวนการ irb แล้วส่งอินพุทบางอย่าง และอ่านเอาต์พุท
ตัวอย่าง:
r = IO.popen("irb", "r+")
r.write "puts 123 + 1\n"
3.times { puts r.gets }
r.write "exit\n"
ตัวแปร r นี้คือออบเจ็กต์ IO หมายความว่า คุณสามารถเขียนและอ่านจากมันได้เหมือนไฟล์ทั่วไป
การใช้ popen หมายความว่าคำสั่งภายนอกจะรันบนกระบวนการของตัวเอง ดังนั้นคุณไม่ต้องรันมันบน thread
ยังมีเมธอด popen3 ใน standard library
ความต่างกับ popen ปกติคือ มันจะแยกเอาต์พุททั่วไปและข้อความข้อผิดพลาดเป็น IO objects แยกกัน
สรุป
คุณได้เรียนรู้วิธีต่างๆ ที่คุณสามารถโต้ตอบกับคำสั่งภายนอกใน Ruby
ระวังกับเหล่านี้!
อย่าส่ง user input ใดๆ เข้าไปในคำสั่งเหล่านี้ เว้นแต่คุณต้องการ ปัญหาด้านความปลอดภัย (security) ในแอป Ruby ของคุณ
ถ้าคุณเรียนรู้อะไรใหม่ กรุณาแชร์บทความนี้เพื่อให้คนอื่นเห็น
ขอบคุณที่อ่าน 🙂