Working with command line arguments in Crystal

Being a compiled language, Crystal is great for creating command line tools. But what’s a command line tool without options? Well, that’s where command line arguments come in.

In line with the goals of the Crystal language, working with command line arguments in Crystal is very similar to doing so in Ruby. The constant even has the same name.

In this tutorial, we’ll be building a simple command line tool that shows the current time, complete with a usage guide.

Let’s start by creating a file that we’ll name cl-tutorial.cr. Since command line tools always have a help page or usage guide, we’ll create one of those first.

A simple way to create a multi-line string in Crystal is with a heredoc. From Crystal’s documentation:

A “heredoc” starts with <<-IDENT, where IDENT is an identifier: a sequence of letters and numbers that must start with a letter. The “heredoc” finishes with a line that starts with IDENT, ignoring leading whitespace, and is either followed by a newline or by a non-alphanumeric character.

In this case, we’ll use STRING as our identifier, and assign it to a variable named usage. Enter this at the beginning of your cl-tutorial.cr file:

usage = <<-STRING
Usage: cl-tutorial [option]

Command:
    time, --time, -t show the current time
    help, --help, -h show this help
    version, --version, -v show version
STRING

 

As I said earlier, Crystal handles arguments pretty much the same as Ruby, with an array with the name ARGV. In this example, we only have one argument, so we’ll be accessing it as ARGV[0].

Since there are 3 different options, we’ll use a case-switch structure to check the argument:

case ARGV[0]
when "time", "--time", "-t"
  puts "The current time is #{Time.now}"
when "help", "--help", "-h"
  puts usage
when "version", "--version", "-v"
  puts "CLI Tutorial v0.1.0"
else
  puts "Unrecognized option"
  puts usage
end

 

Now, running a program with arguments using the Crystal compiler requires a little extra work. You have to add to hyphens (–) between the file name and the arguments themselves. Keep in mind that you only need those hyphens if you’re compiling and running in one step, not when you’re running an executable. So you can either run this with the help option this way:

$ crystal cl-tutorial.cr -- -h

or this way:

$ crystal build cl-tutorial.cr
$ ./cl-tutorial -h

and you should see the help menu output.

The app mostly functions now, but it has one critical error. If you were to run this program without any arguments, you’d get an error, due to ARGV being an empty array. In Ruby, this would trigger the else clause of the case-switch statement, so it’d be ok. However, because Crystal is statically typed, ARGV[0] on an empty array triggers an out of bounds error.

This is easy enough to fix however. We’ll create an if-else statement, and place our current case-switch inside the else of said if-else statement. Add these three lines prior to the case:

if ARGV.size == 0
   puts usage
else

and then add another end after the case. Now, your case statement will only be evaluated if the ARGV array is confirmed to be non-empty. If you’d like, you can confirm this by going back to your command line, and entering:

$ crystal cl-tutorial.cr

without any arguments. The output will be identical to what you saw earlier when you added — -h to the end of the line.

Now our simple program is complete. You can even test out the time function by running:

$ crystal cl-tutorial.cr -- -t

If you’re having any issues, you can compare your program to the original on my github page. Otherwise, feel free to let me know in the comments section.

Leave a comment