people always forget about the humble printf in shell
for example, imagine you want to some operation like sort uniq over space separated values in a string (traditional way to do arrays besides set —): instead of echo into IFS mangling etc consider:
printf ‘%s\n’ $VARS | …
want = pairs? printf ‘%s=%s\n’
and so on

@toast it’s cool but has a bunch of pitfalls if you’re not careful

i.e. people using printf and letting the format-string be user controlled

thankfully -v can only be the first argument, so you can’t accidentally let an attacker write to an arbitrary variable… rare case when the posix parameter order actually helps

(do note: i’m in the 0.1% of shell scripters who even consider it to be a “security boundary” of any sort because of httpsh, so i’m firmly an outlier and i shoudl not have been counted)

@domi @toast i got so confused for a while, checked the posix and coreutils manpage, got even more confused

then checked the bash manpage, and then it made sense

now i wonder why did bash add printf -v var when we had var=$(printf) =w= maybe some edge case i can’t think of

@navi @toast subshells! $() spawns another process, which adds a bunch of overhead, and changes scoping (which can cause issues - it’s very rare, but i’ve encountered cases where printf -v was the only way to assign a variable

btw: modern bash supports ${echo asdf}, which acts exacly the same as $(echo asdf) except it does some magic behind the scenes to not spawn a subshell

@domi @navi oh when did ${} make it in?
@toast @navi i have no hecking clue, @mei has sold me that trick 2 months ago and I’ve been selling it to others ever since
@domi @toast @mei @navi isn’t this just regular code block {} (though it seems without final ;)?
@pj @domi @mei @navi the output of those is generally not captured ^^
also fun: defining functions with ()s rather than {}s make them always run in a subshell, effectively giving you lexical scoping (but with no way to overwrite or add to the upper environment sadly)
@toast @domi @mei @pj

i use that bit on scripts quite a bit, specially combined with set -e

make subshell, set -e, run a bunch of commands i don't want to individually error check

at the end, check exit status of subshell as a whole

(as i'm not a fan of set -e globally for reasons)
@navi @domi @mei @pj yeah I only like -o pipefail globally
which coincidentally is in POSIX now

@domi @toast yea i’m aware of subshells

but also there’s no actual reason a shell couldn’t see that a certain command doesn’t cause side effects, then just posix_spawn() things directly, doesn’t even seem too hard to implement if you keep track of your “env-altering” builtins

(also yes ${... ;} it’s useful but a) i hate how it has the same syntax as variable expansion and b) you need to end with the semi-colon iirc due to (a))

@navi @toast yeah you need the semicolon and load-bearing spaces ^^ you can tell i haven’t used this one much because I forgor

the variable assignment thing is an unfortunate side-effect… there’s a bunch of shell features I would write differently if I were designing them myself. maybe one day ;)

@domi @toast i am already so deeply annoyed by `$()` and `$(())` meaning different things making parsing a pain
@navi @toast tbf you need look-aheads for a few other things anyways (like backslashes) so I don’t think it’s that outrageous. still sorta annoying
@domi @navi join my efforts on the tosti shell (one day) ^^

@navi @domi @toast

$() strips trailing newlines. It would need to be var=$(printf 'foo\nx'); var=${var%x}