เพื่อ master command line จริงๆ ต้อง master โปรแกรม utility เล็กๆ โหล่หรือร้อยหลายๆ ตัว แต่ละตัวทำงานต่างกันหน่อย อาจ overwhelming ได้
โชคดีที่แทนที่ single-purpose tools หลายตัวด้วย general-purpose programming language เช่น Ruby ได้ ใช้ Ruby knowledge ที่มีอยู่ level up command-line-fu
โพสต์นี้พาคุณผ่าน fundamentals ของ Ruby เป็น command-line swiss army knife ไม่ได้ bombard ด้วย clever one-liners แต่มาดูว่าดีไซน์จริงยังไง เพื่อนำไปแก้ปัญหาของตัวเอง
ใช้ Ruby จาก command line
แน่นอนคุณรู้ว่ารัน Ruby programs จาก command line ได้:
$ ruby myprogram.rb
แต่รู้ไหมว่า pipe code เข้า Ruby ให้ execute ได้?
$ echo "puts 2+2" | ruby
4
มีประโยชน์กว่านั้นคือส่ง code เป็น command-line argument นี่คือสิ่งที่เราจะใช้เวลาวันนี้
$ ruby -e 'puts 2+2'
4
Hey Newbies
ตัวอย่างข้างต้นสับสนไหม? อาจเพราะไม่คุ้น pipes และ redirection ดูโพสต์แนะนำดีๆ เราจะใช้ pipes เยอะด้านล่าง
ทำงานกับ Input
Command-line tools ส่วนใหญ่รับ data ประมวลผล แล้ว吐กลับออกมา
เรามีสองทางดีๆ สำหรับ input: command-line arguments และ STDIN มาดูแต่ละอย่าง
Command-line arguments
ส่ง command-line arguments ได้ไม่จำกัด ใส่หลังทุกอย่าง:
$ ruby -e '' arg1 arg2 arg3 etc
Arguments เก็บอยู่ใน array ARGV ตัวอย่าง dump ทั้ง array:
$ ruby -e 'puts ARGV.inspect' apples bananas pears oranges
["apples", "bananas", "pears", "oranges"]
ควรทราบ: นี่คือพฤติกรรม Ruby ปกติ ไม่มี magic แค่เพราะใช้ command line ดูโพสต์เกี่ยว ARGV รายละเอียด
Dumb example
จินตนาการว่าผม ego มาก สิ่งสำคัญคือรู้ว่าชื่อผมถูก mention บนเว็บกี่ครั้ง ใช้เทคนิคข้างต้น one-liner คำนวณได้สำหรับ webpage ใดๆ:
$ ruby -e "require 'open-uri'; puts open(ARGV.first).read.scan(/starr/i).count"
STDIN
Command-line arguments ดีแต่สำหรับค่าสั้นๆ ไม่จะเอาไว้ input unabridged text of Moby Dick ใช้ STDIN แทน
ถ้าไม่คุ้น STDIN อย่าตกใจ สำหรับเรา behave เหมือนไฟล์เปิดอ่าน
ตัวอย่าง pipe text เข้า Ruby script อ่านจาก STDIN พิมพ์หน้าจอ:
echo "bananas!" | ruby -e "puts STDIN.read"
bananas!
Input ข้อมูลเยอะได้ด้วย cat ตัวอย่าง用 first method (available บนไฟล์) ดับบรรทัดแรกๆ:
cat moby.txt | ruby -e "puts STDIN.first(3)"
Call me Ishmael. Some years ago--never mind how long precisely--having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of
Dumb example
ตอนรู้ consume STDIN แล้ว rewrite dumb example ข้างบน แทนที่จะ用 Ruby fetch webpage 用 curl Ruby แค่ pattern matching:
curl | ruby -e "puts STDIN.read.scan(/starr/i).size"
STDIN with syntactic sugar!
เวลาทำงานกับ STDIN มักต้อง loop ทุกบรรทัด จินตนาการอยากได้ file extension ทุกไฟล์ใน directory ทำได้แบบนี้:
ls | ruby -e 'STDIN.each_line { |l| puts l.split(".").last }'
rb
rb
csv
STDIN loop common Ruby มี shorthand รัน script ด้วย flag -n Ruby จะ loop ทุกบรรทัดใน STDIN อัตโนมัติ บรรทัดปัจจุบันอยู่ใน global variable $_
เขียนใหม่ได้:
ls | ruby -n -e 'puts $_.split(".").last'
ขึ้นอยู่กับคุณจะใช้ shorthand หรือไม่ แน่นอนเขียนโค้ดน้อยลง แต่ต้องจำ arbitrary facts เยอะขึ้น เช่น -n และ $_
ทำงานกับ Output
สถานการณ์แบบนี้มักอยากเขียน output ไป STDOUT ให้ flexibility สูงสุด pipe output ไปโปรแกรมอื่นหรือ redirect ลง disk ได้
ข่าวดี: ใช้ print commands เดิมที่คุ้นเคย หนึ่งอย่างตระหนัก: puts เพิ่ม newline อาจจะ/อาจไม่ใช่สิ่งที่ต้องการ
puts "hello world" # ส่ง "hello world\n" ไป STDOUT
print "hello world" # ไม่เพิ่ม newline
รวมทั้งหมดเข้าด้วยกัน
Honeybadger base อยู่ Washington state US หมายความว่าเราจ่าย sales tax ทุก paying customer ที่อยู่ Washington ด้วย
Simple มาก แต่เรามี CSV file ทุก transaction ในปีหนึ่ง คล้ายๆ นี้:
1/1/2015,100.00,WA
1/1/2015,50.00,NY
เพื่อ sum ทุก transactions ใน Washington one-liner แบบนี้:
$ cat cc.csv | ruby -e 'puts STDIN.inject(0) { |sum, x| date, amount, state = x.split(","); state.strip == "WA" ? sum + amount.to_f : sum }'
จำไว้: วิธี parse CSV นี้ sloppy มาก ไม่ส่งโค้ดแบบนี้ production แต่ความสุขของ tiny programs แก้ one-off problems คือ ignore edge cases ได้ นี่คือสิ่งที่ทำที่นี่