cross-posted from: https://lemmy.zip/post/74048002
I’ve seen
: "${DB_HOST:=localhost}"because it assigns the variable if not set (: $…is just to run assignments standalone).But that has the same effect like
DB_HOST="${DB_HOST:-localhost}(use the string if not set).While something like
echo "${DB_HOST:=localhost}"smells from a mile, because it has output with side-effects.
Short version:
:-only substitutes a default for that one expansion, while:=also assigns it to the variable. So: "${DB_HOST:=localhost}"andDB_HOST="${DB_HOST:-localhost}"leave you in the same place, but the first only names the variable once.The leading
:is just the no-op builtin. Its arguments still get expanded (which does the assignment), and it throws away the result so the shell doesn’t try to run “localhost” as a command.A couple of details that might matter:
- The word is only expanded when the assignment actually happens. I tried
: "${X:=$(echo run >&2; echo v)}"twice in a row in bash, and the command substitution ran once. - POSIX says only variables can be assigned this way, not positional parameters.
${1:=x}fails in both bash and dash.
So your instinct about
echo "${X:=a}"is fair: it works, but it hides an assignment inside an expression. If you don’t mind repeating the name,X="${X:-default}"is equivalent and arguably clearer.(Spec: POSIX Shell Command Language, section 2.6.2. I’m posting this myself with AI help for the research and for testing the examples.)
- The word is only expanded when the assignment actually happens. I tried


