> That second example is write-only code,

Tacking on the `-d ''` flag to `read` makes it write-only!?

> In bash, that's a 50-line monstrosity. In Ruby (or Python, or even Perl), it's ~5 clean, readable lines.

As an example, let's take the input, batch it in to groups of 5, filter-out files that don't contain "token1" or "token2", and pass those as a NUL-separated list to stdin of another binary.

I wrote that up. It came out to 10 lines in either Ruby or Bash. https://pastebin.com/EksRxmVJ It's certainly not a "50-line monstrosity"

> Again, in another language, that's just a repeated application of a shellescape function as you're composing things. In bash... I have no idea.

In Bash, "shellescape" is pronounced "printf %q" (and shelljoin is pronounced "printf '%q '"). In Bash, that's similarly just repeated application of printf %q:

base_cmd=('whatever' 'arg with space')
nested_cmd=(sh -c "$(printf '%q ' "${base_cmd[@]}")")
ssh user@hostname "$(printf '%q ' "${nested_cmd[@]}")"

For direct comparison with Ruby:

escaped=$(printf '%q ' "${base_cmd[@]}")
escaped=base_cmd.map{|a|shellescape(a)}.join(" ")
escaped=shelljoin(base_cmd)

Or, Bash 4.4 gained @Q as a shorthand for that:

base_cmd=('whatever' 'arg with space')
nested_cmd=(sh -c "${base_cmd[*]@Q}")
ssh user@hostname "${nested_cmd[*]@Q}"

> In most scripting languages, you've got functions like Pathname#relative_path_from(dir) and Pathname#clean_path.

You mean $(realpath --relative-to=DIR) and $(realpath)?

> Or how about a "recursively collapse empty or pseudo-empty directory" function? ... it's ~10 lines of Ruby. Try to implement it in Bash!...

And it's 6 lines of Bash?

find \( -not -type d \
-not -name .DS_Store \
-not -name Thumbs.db \
-not -name Desktop.ini \
-not -name '._*' \
\) -printf '%h\0%p\0' | sort -uz

Or were you looking for a listing of pseudo-empty directories?

comm -z23 <(find -type d -print0 | sort -z) \
<(find \( -not -type d \
-not -name .DS_Store \
-not -name Thumbs.db \
-not -name Desktop.ini \
-not -name '._*' \
\) -printf '%h\0%p\0' | sort -uz)

Like I said, there are things that are hard to do in Bash, but I'm not certain you've pointed any of them out.

> There is a reason that the default kind of cloud-init script is a YAML file, rather than a Bash script.

And there's also a reason why a "GNU/Linux distro" is essentially a giant pile of shell scripts (maybe embedded in to Makefiles).

> a reason why every /bin/init that isn't sysvinit has soundly beaten sysvinit.

I mean, I'm happy with systemd on my systems, but: OpenRC is a giant pile of shell scripts.

> the parts where you do file manipulation are abstracted out, ... through domain-specific file-manipulation binaries (dpkg)

Great example, because the core of Debian is the "rules" file for each package, which is... a Makefile, containing shell.

Or, look at Arch Linux, the core of which is the "PKGBUILD" file for each package, which is... a Bash script.

> or through pre-written and thoroughly-checked file-manipulation Bash scripts (autotools).

Autoconf is targeting shells that aren't Bash. It's pre-written and thoroughly-checked shell that contains wonky workarounds, in order to be compatible with buggy shells from 25 years ago.

If you're assuming "modern shell / utilities", file manipulation in Bash isn't hard.