Ruby for "shell scripting"
Back to Ruby.
Ruby is a really good Unix citizen and I think it’s woefully underused as a general-purpose "scripting" language.
Anyone familiar with Perl should be delighted to know that Ruby has a lot of
the same convenience features (which, in turn, were lifted from older tools).
For example, /pattern/ for regular expressions, crazy special variables like
$_, $., and $!; sigils (though '$' and '@' have a different meaning),
one-line conditionals in the form of <statement> if <predicate>, and AWK’s
BEGIN and END blocks.
Like Perl, Ruby can be used as a better AWK or Sed for field splitting
or regex match substitution. Ruby even has in-place editing with the
-i command line option like some flavors of Sed!
Ruby is shockingly good at one-liners.
It’s true that Ruby was written with an explicit focus on Object-Oriented Programming. But you can ignore that completely like I do!
I think this ancestral timeline may be interesting:
-
1977 AWK by Aho, Weinberger, and Kernighan
-
1987 Perl by Larry Wall
-
1995 Ruby by Yukihiro Matsumoto
(Matz should have waited two more years to release Ruby in order to make a perfect decadal progression, right?)
(And since I know you’re curious, Python was released in 1991 by Guido van Rossum. I don’t include it in the list above because I feel it occupies a different scripting language niche. By way of example, Python is famously awful for one-liners.)
I know the whole Ruby on Rails thing has tended to overshadow the language itself. But would it surprise you to learn that, as a web developer and Ruby enthusiast, I have never even touched Rails once? I’ve used Ruby for web programming a bit, but mostly for command line programs, where it is a comfortable fit.
Versus shell
Sometime in the last decade, I’ve become disillusioned with using various flavors of shell, AWK, and Sed to write my scripts for my Linux/BSD systems. In fact, I don’t even like cobbling things together with Core Utils unless doing so saves me a lot of time.
Likewise, I have sworn off writing anything in shell that is longer than a few lines. Pursuing the purity of a "true POSIX shell script" is, sadly, a game I don’t think is even worth playing. See The paradox of POSIX.
Increasingly, I’m using Ruby in place of the shell for even small scripts because sometimes they turn into larger scripts and I end up re-writing them anyway.
Versus Sed and Awk
Sed and Awk, quite simply, have let me down in various ways.
I absolutely love the concept of Sed as a tool driven by single-letter commands. And while you can write entire games in Sed, it’s really not programmable without jumping through enormous hurdles in the tradition of the best esoteric puzzle languages.
Awk must have been absolutely brilliant when it was released. The concept is wonderful. The Awk Programming Language book is a masterpiece. And I write quite a bit of AWK at at one point, thinking I had found the One True Command Line Tool. But AWK doesn’t compose, a term I now use after reading A Tutorial for the Sam Command Language by Rob Pike. This is more than merely frustrating - it’s an impediment to writing truly great, compact, idiomatic shell-style scripts.
But the final reason I stopped relying on these tools is that Awk and Sed come in different flavors and support different features. This might be fine if I always used one single platform, but I simply move between platforms too often and have been burned by Awk and Sed incompatibilities multiple times. It has been a massive headache each time.
Versus Perl
As for Perl, all I can say is that I loved it in the late 1990s. The community was fun and I thought the language was really fun and clever. I can think of no stronger current-day endorsement for Perl than the fact that OpenBSD uses Perl in the base system (marc.info) because it is extremely stable and relatively easy to package.
But even when I liked it, I found it hard to justify some of the uglier parts of Perl. I’m not talking about its oft-maligned visual appeal, but the scalar vs list contexts and the absolutely horrendous subroutine calling convention.
Versus Tcl
On this subject, I think it’s worth mentioning: I had a love affair with Tcl for a couple years, but ran into issues trying to force it into weird situations for which it was not well suited such as numerical computing. Also, like Forth, it’s easy to get your brain wrapped in knots if you try to get too "meta" with Tcl.
Having said that, Tcl is very shell-like in the sense that everything is a list of strings. The truth is, it’s been long enough that I can’t really give a good critical run-down of the language.
It’s hard to jump back into Tcl after being away for a while because the way the language works is so unique. That’s not a knock on the language, but perhaps a point in favor of Ruby anyway.
You can ignore the OOP, unless you’re into that sort of thing
When I entered the programming field as a profession, Java was hot, hot, hot. So I learned Object-Oriented Programming in the Java style and thought that was pretty much the be-all and end-all of professional programming!
A full criticism of OOP as the final evolution of program organization would turn this card into a full-blown rant, which I don’t need to do here. Besides, others have done a great job of it already. In fact, I’m currently reading a great one in the form of the last couple chapters of C.J. Date’s epic An Introduction to Database Systems.
Suffice it to say that Ruby’s OOP features are not what keeps me using the
language. In fact, I’ve learned to write Ruby in a very imperative
(top-to-bottom execution like a recipe) fashion. I do use functions (which are
just global methods defined with def) when helpful. I also sometimes write
def methods in an impure "subroutine" manner by accessing global variables defined
with the $ sigil. Totally appropriate for small "shell-sized" tasks.
For an example of this style, see A text editor as a user interface.
Mind you, instantiating standard Ruby library classes and calling methods on
them doesn’t bother me in the slightest and I like foo.bar() syntax just
fine. It’s comfortable and familiar.
Ruby basically has JavaScript’s delightful array/object syntax these days
I am an unapologetic fan of JavaScript’s terse syntax for creating arrays and objects (and dictionaries/hashes). See My JavaScript: The Good Parts.
Creating an array and accessing the first element:
// JavaScript: var a = [1,2,3]; a[0]; // 1 # Ruby: a = [1,2,3] a[0]; # 1
Creating an object/hash and accessing its elements:
// JavaScript
foo = { thing: 'Bar', size: 20 };
foo[thing] // Bar
foo.size // 20 (dot syntax)
# Ruby
foo = { thing: 'Bar', size: 20 };
foo[:thing] # Bar
foo[:size] # 20
Note two things:
-
The ':' sigil indicates a symbol in Ruby.
-
You can’t use the "dot syntax" to access items in a Ruby hash - you need to make a full-blown object for that.
(Why can’t you use the dot for hash item access? Because the '.' is for sending a message in Ruby - which is trivia you don’t really need to know to write perfectly good Ruby scripts.)
Ruby also has awesome Perl-like shortcuts such as %w for creating a quoted
list, etc., so it has the potential to be even better than JS. For example:
%w[foo bar baz] # Creates array: ["foo", "bar", "baz"]
I think this is a great example to end on.
Final thoughts
To me, a general purpose scripting language should do common tasks with as little ceremony as possible. That’s something AWK brought to the Unix command line in 1977 and Ruby continues to bring in 2026.
Is it perfect? Heck no. Ruby has got some weird edge cases and plenty of cruft. It has an entire core paradigm I prefer to ignore. The standard library and occasionally even its syntax continues to grow and evolve over time, so looking stuff up can be annoying. (Though I find that the changes tend to be good and additive in a very pragmatic way. The language has become more pleasurable to use over time, not less.)
Like Python, there’s a whole ecosystem around managing Ruby versions for various projects. So that’s a whole thing if you’re into 3rd party stuff, which I’m not. I don’t even tend to use any Gems, just the core libraries, which are quite extensive!
I write very simple Ruby that seems to keep working across every version and upgrade I’ve ever encountered in the wild. So yes, on one hand, it’s not as stable as Perl 5, but on the other hand it’s still improving in ways that won’t break your existing stuff.
Ruby’s performance has come leaps and bounds since the early days. The 3.0 release (2020) was a major boost and it’s continued to get better since. Script start times have ceased to be something I perceive with my feeble human senses.
See also Using Ruby as a Calculator.