Để thực sự làm chủ command line, bạn phải làm chủ chục — nếu không phải trăm — chương trình tiện ích nhỏ. Mỗi cái làm mọi việc hơi khác nhau. Có thể khá overwhelming.
May mắn thay, có thể thay thế nhiều tool đơn dụng này bằng ngôn ngữ lập trình đa năng như Ruby. Cách đó bạn dùng kiến thức Ruby đã có để level up command-line-fu.
Bài này sẽ đưa bạn qua cơ bản dùng Ruby như con dao bọc swiss army knife cho command line. Tôi không sẽ ném bạn đầy one-liner khéo léo. Thay vào đó chúng ta sẽ xem mọi thứ thực sự hoạt động như thế nào, để hy vọng bạn áp dụng kỹ thuật này giải quyết vấn đề riêng.
Dùng Ruby từ command line
Chắc bạn biết chạy Ruby program từ command line như sau:
$ ruby myprogram.rb
Nhưng bạn có biết có thể pipe code vào Ruby để thực thi?
$ echo "puts 2+2" | ruby 4
Hữu ích hơn là khả năng truyền code làm command-line argument. Đây là điều chúng ta sẽ dành thời gian hôm nay.
$ ruby -e 'puts 2+2' 4
Hey Newbies
Các ví dụ trên hơi confusing? Có thể vì bạn chưa quen với pipes và redirection. Xem bài này cho intro tốt. Chúng ta sẽ dùng pipes nhiều bên dưới.
Làm việc với input
Hầu hết command-line tool nhận data, xử lý, rồi吐 ra.
Chúng ta có hai option tốt cho input: command-line arguments và STDIN. Hãy xem từng cái.
Command-line arguments
Bạn có thể gửi bao nhiêu command-line arguments tùy thích cho script. Chỉ để sau mọi thứ khác:
$ ruby -e '<your code here>' arg1 arg2 arg3 etc
Các argument này lưu trong array ARGV. Trong ví dụ dưới, tôi dump cả array để bạn thấy có gì.
$ ruby -e 'puts ARGV.inspect' apples bananas pears oranges ["apples", "bananas", "pears", "oranges"]
Lưu ý đây chính xác cách Ruby luôn hoạt động. Không có phép màu chỉ vì dùng command line. Xem bài về ARGV cho chi tiết.
Ví dụ ngớ
Tưởng tượng một giây tôi siêu ego. Điều quan trọng nhất là biết tên tôi được nhắc bao nhiêu lần trên web. Dùng kỹ thuật trên, tôi dễ dàng viết one-liner tính toán cho bất kỳ webpage nào.
$ ruby -e "require 'open-uri'; puts open(ARGV.first).read.scan(/starr/i).count" <url here>
STDIN
Command-line arguments tuyệt, nhưng chỉ tốt cho giá trị ngắn. Bạn không muốn dùng cho – ví dụ — input toàn bộ Moby Dick không rút gọn. Cho đó ta dùng STDIN.
Nếu bạn không quen STDIN, đừng sợ. Với mục đích của chúng ta, nó hoạt động như file mở để đọc.
Đây là ý tôi. Trong ví dụ dưới, chúng ta pipe text vào Ruby. Script Ruby đọc từ STDIN và in ra màn hình.
echo "bananas!" | ruby -e "puts STDIN.read" bananas!
Chúng ta dễ dàng input lượng data lớn hơn bằng cat. Ví dụ dưới dùng phương thức first có sẵn trên file bất kỳ để lấy vài dòng đầu:
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
Ví dụ ngớ
Bây giờ biết cách consume STDIN, hãy viết lại ví dụ ngớ trên. Thay vì dùng Ruby fetch webpage, dùng curl và chỉ dùng Ruby cho pattern matching.
curl <MY URL> | ruby -e "puts STDIN.read.scan(/starr/i).size"
STDIN với syntactic sugar!
Khi làm việc với STDIN, rất thường phải loop qua mỗi dòng input. Tưởng tượng tôi muốn lấy extension file cho mọi file trong thư mục. Đây là cách dùng vòng lặp STDIN bình thường:
ls | ruby -e 'STDIN.each_line { |l| puts l.split(".").last }' rb rb csv
Vì vòng lặp STDIN phổ biến, Ruby cung cấp shorthand. Nếu chạy script với flag -n, Ruby tự động loop qua mỗi dòng trong STDIN. Dòng hiện tại ở global variable $_.
Vì vậy có thể viết lại ví dụ trên như sau:
ls | ruby -n -e 'puts $_.split(".").last'
Tùy bạn có muốn dùng shorthand hay không. Mặc dù nó viết ít code hơn, nhưng cũng nghĩa là bạn phải nhớ thêm 사실 tùy tiện như -n và $_.
Làm việc với output
Trong tình huống như này, bạn thường muốn ghi output ra STDOUT. Điều này cho sự linh hoạt nhất vì cho phép pipe output vào chương trình khác hoặc redirect ra disk khi cần.
Tin tốt là bạn dùng cùng lệnh print quen thuộc. Một điều cần lưu ý là puts thêm newline, có thể hoặc không phải thứ bạn muốn.
puts "hello world" # gửi "hello world\n" tới STDOUT print "hello world" # không thêm newline"
Gộp tất cả lại
Honeybadger đặt tại Washington state, US. Nghĩa là chúng tôi có đặc quyền thu sales tax cho mọi khách hàng trả tiền cũng sống ở Washington.
Tôi đơn giản hóa khá nhiều, nhưng cơ bản chúng tôi có CSV file mọi transaction năm. Trông như sau:
1/1/2015,100.00,WA 1/1/2015,50.00,NY
Vì vậy để lấy tổng nhanh cho tất cả transaction ở Washington, tôi dùng one-liner như sau:
$ cat cc.csv | ruby -e 'puts STDIN.inject(0) { |sum, x| date, amount, state = x.split(","); state.strip == "WA" ? sum + amount.to_f : sum }'
Nhớ rằng đây là cách parse CSV horribly sloppy. Tôi không bao giờ gửi code này ra thế giới thực. Nhưng một niềm vui viết chương trình nhỏ giải quyết one-off problem là bạn bỏ qua tất cả edge cases. Đó là điều tôi làm ở đây.