I think it's a really good sign of growth and healing that I'm finding myself increasingly repulsed by the kind of portability extremism that once compelled me.
One of the biggest and worst examples was shell scripts. /bin/sh was the Bourne shell in UNIXv7 (prior to that, there was the Thompson shell, and thankfully I managed to keep my mind cancer from metastasizing further backwards in time to try to achieve compatibility with that shell too). After the Bourne shell, every /bin/sh on every system was a Bourne-like shell, and if you thought that meant you could just write something that worked, take a glance at:
GNU Autoconf's Portable Shell documentation.
Sven Mascheck's various pages.
Paul Jarc's "lintsh" notes.
Ubuntu's "dash"-as-/bin/sh guide.
and others which you can find from there.
Now, a healthy person simply rejects this problem space. But for years, I was obsessed with writing shell scripts which would work on all /bin/sh still in production. It started as a growing annoyance with so many programs depending on bash - I was otherwise happily using a system with a more minimal shell at the time, and the limitations of my beloved Nokia N900 as a pocket Linux device gave me some real reason to prefer "reducing bloat" back then. Of course if it mattered to me, my compassion generalized it to everyone else in the same boat (everyone real or imagined... and in this case, mostly imagined). Then one day in the first year of my career as a software developer I got into a small argument with a coworker about them mandating #!/bin/bash instead of #!/bin/sh in our shell scripts - after he asserted that it was unreasonable to expect developers to remember what is or isn't a bashism, my maladaptive narcissistic cope reflexively kicked into full gear and now I had something to prove.
I still remember bits of that evening after work. It's... kinda horrifying looking back on it, because I was aware of what was happening in my mind. I was aware that I was basically starting to involuntarily, compulsively terraform my own preferences and values about shell scripts, from the modest and real and practical "I just want scripts to run on my N900s (BusyBox ash implementation for /bin/sh), and maybe also my Debian boxes (dash for /bin/sh)" to some perverse "principled" stance with poorly-defined scope which was divorced from any specific concrete goals. I had seen this runaway snowballing of artificial nitpicky values happen in my mind before, and I recognized that what I was doing in my head was feeding it, that it was happening again or that I was making it happen again, and I felt some conflict with that, I could see how it was bad... but back then I didn't know how to do anything about it. I didn't know how to diffuse those wants back then. I could in some technical sense, have chosen to not do it, but I couldn't stop wanting to, and I couldn't stop rationalizing it.
So I became the kind of guy that basically had every caveat mentioned on the above pages memorized. I even went as far as having a Solaris 10 VM, some old Android phones, and a PDP emulator running UNIXv7, so that I could test things not mentioned or not elaborated on those pages. But since it's really costly to remember so much trivia, I only remembered the caveats themselves, not necessarily which shells/systems they applied to. I could tell you off the top of my head "well you see, on some shells, 'set -e' will not affect the code inside functions", but I couldn't tell you which shells - I just had the caveats grouped by
"only matters on systems that no one runs anymore",
"only matters in situations you/we will never need to be compatible with (like Solaris 10's /bin/sh)",
"only matters if you want portability on Windows ports of UNIX-y shell stuff",
"only matters if you want portability beyond just Linux", and
"only matters if you want portability beyond just 'bash'".
I also used to have a little template for shell portability disclaimers that I would add to my shell scripts, deleting/re-adding lines as-needed:
# This script is compatible with Bourne and POSIX shells. # EXCEPT for the following exceptions (last verified on YYYY-MM-DD): # Comments (Appeared in 1981, still not universal around 1987) # Functions (First appeared in SVR2 Bourne shells in 1984) # `mkfifo` (First appeared sometime circa 1984, possibly earlier; unsure) # `test -p` (First appeared in SVR1 Bourne shell in 1983). # `wait` exit status (Missing in Almquist shell until 4.4BSD in 1993) # `hash` builtin (First appeared in SVR2 Bourne shells in 1984) # `type` builtin (First appeared in SVR2 Bourne shells in 1984) # $() is used instead of `` (not supported by some ancient Bourne shells) # `shift` when no positional parameters (broke some old MIPS RISC/os shells) # ${VAR%glob} substitution (Solaris (<= 10) /bin/sh does not support it) ...
That version of me looked at my old esceval.sh with pride, as if it was important or worthwhile. It tries to use modern-ish POSIX shell features but falls back to portable shell if it must. Basically every single line has at least one detail that is a deliberate portability choice. Almost every degree of freedom has been optimized for portability (and then some performance optimization within that) - change almost anything and it's probably less portable.
I revisited "esceval" for the first time in years this past week, and I noticed something really nice. I no longer have enough appetite for this portabiliy stuff. I'm too acutely aware, down to my motivating emotions, that it's a waste of my life. I'm once again in touch with actual concrete use-cases and benefits that have high odds of coming up in my life. I've re-learned to value myself and my goals more than this portability shit.
So I'm going to delete the portability fallback from "esceval.sh". I'm done trying to figure out what the portability fallback looks like for the other esceval pieces that I still want to finish. Unless I'm being compensated better than I can get elsewhere, I'm never again going to lift a finger to support Solaris 10 /bin/sh, or Android phones lobotomized to the point of not having a "printf" command in their shell, or anything else that isn't at least POSIX-compatible shell. And even then I'd suggest implementing that by writing a backpiler from modern shell to older. Maybe I'll answer portability questions if I still remember the answer and can say it off the top of my head - I enjoy helping people after all.
And it goes deeper than that. I'm very done giving Bourne-style shells nearly as much time and effort as I've given them so far. They're good DSLs for redirecting file descriptors and sorta okay DSLs for invoking and managing processes, and that's about it. As an unfortunate practical matter, Bourne-style shell is one of the most widely deployed programming language families, so if the goal is "I want to be able to give this tiny CLI to a coworker so they can run it on their machine with minimal human hassle", it can be nice to have a #!/bin/sh implementation (but so is having a couple statically compiled executables for the common platforms and a cross-compiler ready for the rest, or a Python script, or [...]).
It'll take me some time to figure out exactly where that balance is, and to fully unlearn the various hangups and compulsions that I've built up which motivate writing a /bin/sh script instead of something else, but what I've been doing so far definitely ain't that balance, ain't even close, and now I finally have a strong-enough hunger for breaking free and moving in the direction of that healthier balance.











