floof.org

Simon Tatham mastodon (AP)

In bash, writing ${var?} instead of just ${var} or $var means if var isn't defined then bash will throw an error and _not_ execute your command, instead of expanding it to "" and carrying on.

mv file1 file2 $subdir # oops, I overwrote file2
mv file1 file2 ${subdir?} # error message instead of disaster

My favourite use of this is for example commands in documentation, with placeholders for the user to fill in. Then it's OK if a user accidentally copy-pastes it _without_ filling them in!

1 3
Richard Hendricks mastodon (AP)
I'm just a moron, but why would anyone want the default to be this vs the opposite?? "If you don't recognize this, STOP" seems way more sensible.
Simon Tatham mastodon (AP)
@hendric don't ask me! That decision was made literally before I was born.
Rob Russell mastodon (AP)

I sometimes use `set -e` at the top of a script but it does so many unexpected things.

This tip is a more practical option in a lot of cases, thanks!

Wanja mastodon (AP)

@Rob_Russell `set -e` is errexit, which is also nice but something else. To have ${var} error on undefined, you need `set -u`.

I reflexively start all of my scripts with ol' reliable `set -euo pipefail`.

1 1
Simon Tatham mastodon (AP)

@muvlon @Rob_Russell thank _you_, I hadn't been aware of 'set -u' to make this behaviour the default!

(@hendric, I think that's what you were just asking for!)

Johan Wärlander 🦀 mastodon (AP)
@muvlon @Rob_Russell @hendric I've been using 'set -u' in pretty much all my scripts, but '${var?}' is great, as you say, for putting in examples & snippets intended to be copy-pasted! 🚀
Nogweii mastodon (AP)

@muvlon @Rob_Russell @hendric if y'all didn't know, `set -o pipefail` is also very handy - it means that earlier command's exit codes won't be overridden by later commands that have been piped. That is $? is non-zero if any command in the pipeline is.

So `fail | grep blah` still results in $? being 1 (or whatever else)

Wanja mastodon (AP)

@nogweii @Rob_Russell @hendric Yep! There are some pitfalls though, which is why I sometimes do opt out of pipefail.

Example: `if foo | grep -q "bar"; then ... ` is something you might like to use. The -q makes grep quiet, i.e. it won't print matches. However! It will also make grep exit (successfully) on the first match. That by itself is fine, but it also closes grep's stdin, so if `foo` checks for errors, it might fail now. And with pipefail, your `if` is now never taken!

Simon Tatham mastodon (AP)

@muvlon @nogweii @Rob_Russell @hendric yes, that is awkward. You'd almost like 'died of SIGPIPE' to be an exception, not counting as failure for pipefail purposes. Because it doesn't reflect badly on the pipe writer – it only means the reader lost interest, and it's up to the reader whether that was a bad thing.

(Similarly, why does Python default to printing a huge stack-trace on SIGPIPE, bah)

But one problem: SIGPIPE _not on stdout_ might need to be an exception to the exception.

I like to harden my scripts with "set -Eeuo pipefail". Makes maintenance a lot easier over time.
DaCool mastodon (AP)

I basically use the unofficial "bash strict mode" for this.

http://redsymbol.net/articles/unofficial-bash-strict-mode/

Though I prefer the flavour at the bottom of this blog post.

https://olivergondza.github.io/2019/10/01/bash-strict-mode.html

What a neat trick, thanks for that. This also works in zsh.
DarkRat mastodon (AP)

rm -rf $foo/$bar

whoops, root's gone

Klaus Stein mastodon (AP)

That's nice :-)

I additionally recommend to use
mv file1 file2 "$subdir"
or better (TIL!)
mv file1 file2 "${subdir?}"

as even if $subdir expands to an empty string the surrounding "…" ensure the result is a third argument to mv.

marco_m_aus_f mastodon (AP)

And you can add an error message after the "?", too 😀

mv "{$1?No sourcefile given}" "{$2?No destination given}"

Edit: See the section on parameter substitution in the advanced bash scripting guide.

https://tldp.org/LDP/abs/html/parameter-substitution.html

This entry was edited (1 year ago)
aburka 🫣 mastodon (AP)
this gets real confusing at $dayjob because in our custom YAML extensions (don't ask) the syntax `${var?}` means to substitute var, unless it's undefined in which case leave it out. We *do* have syntax that does the same as this bash feature, but it's `${var!}` instead
This entry was edited (1 year ago)
just add "set -u" at the top oft the script (like I so since >25 years) ...
Simon Tatham mastodon (AP)

@ovrim yes, so said many other people in this thread. But in the context I mentioned, this doesn't help: if you're writing an example command in documentation, your reader may not be putting it in a script at all (it's at least as likely they'll run it interactively), and if they do, _they_, not you, make the choice of whether to put 'set -u' at the top of the script!

So it's still valuable to have a way to write a statement that's safe even when you don't control its context.

oldherl misskey (AP)
This semantic of ? is the opposite of it in many other modern languages, though.
Simon Tatham mastodon (AP)

@oldherl well, the last thing anyone would accuse bash of (or any other POSIX-derived shell) is being modern!

But bash's use of ? here doesn't seem 180° away from the Rust ? operator, say. Both mean "if there's an error, propagate it to some far-away caller and do not continue running the code that would otherwise follow this statement". Every detail of the error representation is different, but the effect on control flow is identical.

Jyrgen N mastodon (AP)

I use it mostly for

thing=${1?usage: $0 thing}

to have a useful error message as well.

Pau Amma mastodon (AP)
Also works for (a)sh, eg on FreeBSD.
Handsome Bear mastodon (AP)
TIL . thank you for this important mention. It could probably help a lot with:
rm -rf ${var1}/${var2}
gunstick mastodon (AP)
also, always use " around variables. So many more spaces in strings today...
1 1
Will mastodon (AP)
Thank you so much for this info. I went searching and I could not find anywhere else that describes this construction.
This entry was edited (1 year ago)
- mastodon (AP)
I'll have to see if this works in a standard bourne shell. Thank you!
Tobias mastodon (AP)
so its `set -u` but just for one variable? neat!
This entry was edited (1 year ago)
Simon Tatham mastodon (AP)
@Sweetshark I _was_ talking about using it as a shell! When I said 'example commands in documentation', they're often commands that the user will enter interactively, not in a script.
Bebna mastodon (AP)

@leyrer

Uff, in my head I read this directly the other way around, with the ? version being okay with undefined.

Kay Ohtie mastodon (AP)
Dang, first my first SSH client, then how I burnt time when doing data entry phone calls, and now extremely useful bash stuff, haha. This is excellent, thank you for the tip!

mv -t "$subdir" file1 file1

is also possible.

Eckes :mastodon: mastodon (AP)
or use `set -uC`
Thank you all very much for the hints you gave in this thread. I've been using bash for decades and thought that I basically know the important stuff, but obviously I barely scratched the surface. Cool :)
Pippin friendica
@Cryptoparty Köln-Bonn @Simon Tatham Same here. I've even seen this syntax in the bash man page many times, but, I guess because I'm normally looking for something else at the time, it's never really registered that that's what it would be useful for. Thanks!
1