Skip to Content
Course content

32: Building a Command-Line Ruby Script

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've spent a lot of my career writing massive Rails applications, but some of the most useful tools I've ever built are tiny, single-file Ruby scripts. They're perfect for those "I only need to do this once" tasks that are too complex for a one-liner but too small for a full project. Let's build one together.

I've got a directory full of server logs, and I'm tired of using grep when I want to do something slightly more complex—like counting how many unique IP addresses are triggering 404 errors. I want a tool where I can just pass the log file as an argument and get a report.

Hardcoding is a trap

My first instinct is usually to just get it working. I'll start with a basic script that opens a specific file.

# log_parser.rb
lines = File.readlines("production.log")
errors = lines.select { |line| line.include?("404") }
puts "Found #{errors.size} errors."

I run this with ruby log_parser.rb and it works. But immediately, I realize the problem: if I want to check staging.log, I have to open the source code and change the string. That's a terrible workflow. I shouldn't be editing code just to change the input.

Making it dynamic with ARGV

Ruby gives us a constant called ARGV (argument vector). It's just an array of everything passed to the script after the filename in the terminal. Let's try swapping that hardcoded string for the first element of that array.

# log_parser.rb
filename = ARGV[0]
lines = File.readlines(filename)
errors = lines.select { |line| line.include?("404") }
puts "Found #{errors.size} errors."

Now I can run ruby log_parser.rb production.log. Much better. But wait—what happens if I just run ruby log_parser.rb without any arguments? Ruby doesn't complain about ARGV[0] being nil, but File.readlines(nil) throws a TypeError. It's an ugly crash that doesn't tell the user what they did wrong.

Handling the "I forgot the argument" crash

I need a guard clause. If the user didn't provide a file, I should tell them how to use the script and then exit gracefully. I'll use abort here because it prints a message and exits with a non-zero status code, which tells the shell that the script failed.

# log_parser.rb
if ARGV.empty?
  abort "Usage: ruby log_parser.rb [logfile]"
end

filename = ARGV[0]

unless File.exist?(filename)
  abort "Error: File '#{filename}' not found."
end

lines = File.readlines(filename)
errors = lines.select { |line| line.include?("404") }
puts "Found #{errors.size} errors."

Now the script feels like a professional tool. It validates the input and guides the user. But there's still one annoyance: typing ruby before the filename every single time. I want this to feel like a native system command.

Turning a script into an executable

To do this, I need to tell the operating system which interpreter to use. I'll add a "shebang" line to the very top of the file. I prefer #!/usr/bin/env ruby over #!/usr/bin/ruby because the env version finds where Ruby is installed on the specific system, which is crucial if you're using a version manager like rbenv or asdf.

#!/usr/bin/env ruby

if ARGV.empty?
  abort "Usage: log_parser [logfile]"
end

# ... rest of the code ...

Adding the line isn't enough; the file also needs "execute" permissions. I'll run chmod +x log_parser.rb in my terminal. Now, I can run it directly:

./log_parser.rb production.log

If I move this file into a directory in my PATH (like /usr/local/bin), I could just type log_parser production.log from anywhere on my machine. That's the transition from "a Ruby file" to "a command-line tool."




📋 Practical Task

Build a File Word-Count Utility

Create a Ruby script named word_count.rb that acts as a CLI tool. Your script should satisfy the following requirements:

  • Include a shebang line so it can be run as an executable.
  • Accept a filename as a command-line argument.
  • If no filename is provided, use abort to print a usage message (e.g., "Usage: word_count [file]").
  • If the file does not exist, use abort to print an error message.
  • The script should read the file and print the total number of words contained in the file. (Hint: You can use .split on the file content to get an array of words).

Test your script by running chmod +x word_count.rb and then executing it with ./word_count.rb your_test_file.txt.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.