Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

การเขียนเชลล์ด้วยรหัสทับทิม 25 บรรทัด

หากคุณใช้ Linux หรือ Mac ทุกครั้งที่คุณเปิดเทอร์มินัล แสดงว่าคุณกำลังใช้แอปพลิเคชันเชลล์อยู่

เชลล์เป็นส่วนต่อประสานที่ช่วยให้คุณรันคำสั่งในระบบของคุณ

เชลล์โฮสต์ตัวแปรสภาพแวดล้อม &มีคุณสมบัติที่มีประโยชน์ เช่น ประวัติคำสั่งและการเติมข้อความอัตโนมัติ

หากคุณเป็นคนประเภทที่ชอบเรียนรู้ว่าสิ่งต่างๆ ทำงานอย่างไร โพสต์นี้เหมาะสำหรับคุณ!

เชลล์ทำงานอย่างไร

ในการสร้างแอปพลิเคชันเชลล์ของเราเอง มาคิดกันว่าเชลล์คืออะไร:

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

ใช่ ฟังดูธรรมดามาก แต่นี่ไม่ได้ทำให้คุณนึกถึงอะไรเหรอ

หากคุณกำลังนึกถึง pry ถ้าอย่างนั้นคุณพูดถูก!

เชลล์ใน REPL (Read-Eval-Print-Loop) สำหรับระบบปฏิบัติการของคุณโดยพื้นฐาน

เมื่อรู้ว่าเราสามารถเขียนเวอร์ชันแรกของเชลล์ของคุณ :

prompt = "> "

print prompt

while (input = gets.chomp)
  break if input == "exit"

  system(input)
  print prompt
end

สิ่งนี้จะทำให้เรามีเปลือกที่เล็กที่สุด แต่ใช้งานได้จริง เราสามารถปรับปรุงสิ่งนี้ได้โดยใช้ไลบรารีที่แอปพลิเคชันที่คล้ายกับ REPL อื่นๆ ใช้

ห้องสมุดนั้นชื่อ Readline .

การใช้ไลบรารี Readline

Readline เป็นส่วนหนึ่งของ Ruby Standard Library ดังนั้นจึงไม่มีอะไรต้องติดตั้ง คุณเพียงแค่ต้อง require มัน.

ข้อดีอย่างหนึ่งของการใช้ Readline คือสามารถเก็บประวัติคำสั่งให้เราได้โดยอัตโนมัติ

นอกจากนี้ยังสามารถดูแลการพิมพ์พรอมต์คำสั่งและสิ่งอื่น ๆ อีกมากมาย

นี่คือเชลล์ v2 ของเรา คราวนี้ใช้ Readline :

require 'readline'

while input = Readline.readline("> ", true)
  break if input == "exit"

  system(input)
end

ดีมาก เรากำจัด puts . สองอัน เพื่อความรวดเร็ว &ตอนนี้เราสามารถเข้าถึงความสามารถอันทรงพลังจาก Readline . ตัวอย่างเช่น เราสามารถใช้แป้นพิมพ์ลัดเพื่อลบคำ (CTRL + W ) หรือแม้แต่ค้นหาประวัติ (CTRL + R )!

มาเพิ่มคำสั่งใหม่เพื่อพิมพ์ประวัติทั้งหมด:

require 'readline'

while input = Readline.readline("> ", true)
  break                       if input == "exit"
  puts Readline::HISTORY.to_a if input == "hist"

  # Remove blank lines from history
  Readline::HISTORY.pop if input == ""

  system(input)
end

เกร็ดน่ารู้:หากคุณลองใช้โค้ดนี้แบบงี่เง่า คุณจะได้รับประวัติคำสั่งของ pry! เหตุผลก็คือ pry ก็ใช้ Readline และ Readline::HISTORY เป็นสถานะที่ใช้ร่วมกัน

ตอนนี้คุณสามารถพิมพ์ hist เพื่อรับประวัติคำสั่งของคุณ 🙂

การเพิ่มการเติมข้อความอัตโนมัติ

ด้วยคุณสมบัติเติมข้อความอัตโนมัติของเชลล์ที่คุณชื่นชอบ คุณจะสามารถบันทึกการพิมพ์ได้มาก Readline ทำให้การรวมคุณลักษณะนี้เข้ากับเชลล์ของคุณเป็นเรื่องง่าย

เริ่มต้นด้วยการเติมคำสั่งอัตโนมัติจากประวัติของเรา

ตัวอย่าง :

comp = proc { |s| Readline::HISTORY.grep(/^#{Regexp.escape(s)}/) }

Readline.completion_append_character = " "
Readline.completion_proc = comp

## rest of the code goes here ##

ด้วยรหัสนี้ คุณจะสามารถเติมคำสั่งที่พิมพ์ไว้ก่อนหน้านี้โดยอัตโนมัติโดยกด <tab> กุญแจ. ตอนนี้ ไปขั้นตอนต่อไป &เพิ่มการเติมไดเร็กทอรีอัตโนมัติ

ตัวอย่าง :

comp = proc do |s|
  directory_list = Dir.glob("#{s}*")

  if directory_list.size > 0
    directory_list
  else
    Readline::HISTORY.grep(/^#{Regexp.escape(s)}/)
  end
end

completion_proc ส่งคืนรายชื่อผู้สมัครที่เป็นไปได้ ในกรณีนี้ เราเพียงแค่ต้องตรวจสอบว่าสตริงที่พิมพ์นั้นเป็นส่วนหนึ่งของชื่อไดเร็กทอรีโดยใช้ Dir.glob . Readline จะดูแลส่วนที่เหลือ!

การนำวิธีการของระบบไปใช้

ตอนนี้คุณควรมีเชลล์ที่ใช้งานได้พร้อมประวัติและการเติมข้อความอัตโนมัติ ไม่เลวสำหรับโค้ด 25 บรรทัด 🙂

แต่มีบางอย่างที่ฉันต้องการเจาะลึกลงไป ดังนั้นคุณสามารถรับข้อมูลเชิงลึกเกี่ยวกับสิ่งที่เกิดขึ้นเบื้องหลังของการรันคำสั่งจริงๆ

สิ่งนี้ทำโดย system วิธีใน C วิธีนี้จะเพียงแค่ส่งคำสั่งของคุณไปที่ /bin/sh ซึ่งเป็นแอปพลิเคชันเชลล์ มาดูกันว่าคุณจะใช้ /bin/sh . อะไรได้บ้าง ทำใน Ruby

หมายเหตุ :ใช้งานได้บน Linux / Mac เท่านั้น 🙂

วิธีการของระบบ:

def system(command)
  fork {
    exec(command)
  }
end

สิ่งที่เกิดขึ้นที่นี่คือ fork สร้างสำเนาใหม่ของกระบวนการปัจจุบัน จากนั้นกระบวนการนี้จะถูกแทนที่ด้วยคำสั่งที่เราต้องการเรียกใช้ผ่าน exec กระบวนการ. นี่เป็นรูปแบบทั่วไปในการเขียนโปรแกรม Linux

ถ้าคุณไม่ fork กระบวนการปัจจุบันจะถูกแทนที่ ซึ่งหมายความว่าเมื่อคุณเรียกใช้คำสั่ง (ls , cd หรืออย่างอื่น) เสร็จแล้วโปรแกรม Ruby ของคุณจะยุติการทำงานด้วย

คุณจะเห็นสิ่งที่เกิดขึ้นที่นี่:

def system(command)
  exec(command)
end

system('ls')

# This code will never run!
puts "after system"

บทสรุป

ในโพสต์นี้ คุณได้เรียนรู้ว่าเชลล์เป็นส่วนต่อประสานที่เหมือน REPL (คิดว่า irb / pry ) สำหรับการโต้ตอบกับระบบของคุณ คุณยังได้เรียนรู้วิธีสร้างเชลล์ของคุณเองโดยใช้ Readline . อันทรงพลัง ไลบรารีซึ่งมีฟีเจอร์ในตัวมากมาย เช่น ประวัติและการเติมข้อความอัตโนมัติ (แต่คุณต้องกำหนดวิธีการทำงาน)

และหลังจากนั้นคุณก็ได้เรียนรู้เกี่ยวกับ fork + exec รูปแบบที่ใช้กันทั่วไปในโครงการการเขียนโปรแกรม Linux

หากคุณชอบโพสต์นี้ คุณช่วยฉันและแบ่งปันกับเพื่อน Ruby ของคุณได้ไหม จะช่วยให้บล็อกเติบโต &ผู้คนจะได้เรียนรู้มากขึ้น 🙂