Hacker News new | past | comments | ask | show | jobs | submit login

I love using Ruby for shell scripting, but there are also a ton of little nits I have to fix whenever I'm doing it.

For example: Ruby has no built-in for "call a subprocess and convert a nonzero exit status into an exception", ala bash `set -e`. So in many of my Ruby scripts there lives this little helper:

    def system!(*args, **kwargs)
      r = system(*args, **kwargs)
      fail "subprocess failed" unless $?.success?
      r
    end
And I can't ask "is this command installed" in an efficient built-in way, so I end up throwing this one in frequently too (in this instance, whimsically attached to the metaclass of the ENV object):

    class << ENV
      def path
        @path ||= self['PATH'].split(':').map{ |d| Pathname.new(d) }
      end

      def which(cmd)
        cmd = cmd.to_s
        self.path.lazy.map{ |d| d + cmd }.find{ |e| e.file? && e.executable? }
      end
    end
I have other little snippets like this for:

• implicitly logging process-spawns (ala bash `set -x`)

• High-level wrapper methods like `Pathname#readable_when_elevated?` that run elevated through IO.popen(['sudo', ...]) — the same way you'd use `sudo` in a bash script for least-privilege purposes

• Recursive path helpers, e.g. a `Pathname#collapse_tree` method that recursively deletes empty subdirectories, with the option to consider directories that only contain OS errata files like `.DS_Store` "empty" (in other words, what you'd get back from of a git checkout, if you checked in the directory with a sensible .gitignore in play)

...and so forth. It really does end up adding up, to the point that I feel like what I really want is a Ruby-like language or a Ruby-based standalone DSL processor that's been optimized for sysadmin tasks.




> Ruby has no built-in for "call a subprocess and convert a nonzero exit status into an exception"

Since Ruby 2.6 you can pass `exception: true` to `system` to make it behave like your `system!`.

https://rubyreferences.github.io/rubychanges/2.6.html#system...


Didn't realize that! That's one snippet I can maybe eliminate now. (As to why I didn't know: the first thing in the RDoc for Kernel#system is still "see the docs for Kernel#spawn for options" — and then Kernel#spawn doesn't actually have that one, because it doesn't block until the process quits, and so returns you a pid, not a Process::Status. I stopped looking at the docs for Kernel#system itself a long time ago, just jumping directly to Kernel#spawn...)

But come to think of it, if Kernel#system is just doing a blocking version of Kernel#spawn → Process#wait, then shouldn't Process#wait also take an exception: kwarg now?

And also-also, sadly IO.popen doesn't take this kwarg. (And IO.popen is what I'm actually using most of the time. The system! function above is greatly simplified from the version of the snippet I actually use these days — which involves a DSL for hierarchical serial task execution that logs steps with nesting, and reflects command output from an isolated PTY.)


Do you have a gist with all your tricks?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: