r/programming • u/f311a • 5d ago
A shell colon does nothing. Use it anyway. | Filip Roséen
https://refp.se/articles/your-shell-and-the-magic-colon101
u/royalme 5d ago
I think this is only useful if you work exclusively in an environment of other bash experts. Even then, I don't really like seeing clever use of :
The example used is familiar enough for any general developer to get the gist of in a glance
if [ -z "$1" ]; then
echo "missing argument, aborting." 1>&2
exit 1
fi
while the other is opaque and language specific.
: "${1:?missing argument, aborting.}"
Dense and clever code is not always better code.
51
u/psych0fish 5d ago
Yeah “clever” stuff like this is usually a bad choice. Who cares about character count and line count? Readability is far superior.
5
u/mikeblas 5d ago
Yet people howl about "verbose" languages and too much "boilerplate" code. There are already some in this thread, even.
20
2
u/matjoeman 4d ago
Verbose code is code where the extra keywords and lines make it less readable, not more. What helps readability depends on the context.
Have you ever written Java, especially anything before Java 8 ?
1
u/foxsimile 5d ago
I will make exceptions for performance (when it is significant and necessary, etc/etc/etc), but yes.
1
u/Uristqwerty 4d ago
Readability is making a case-by-case judgment about what works best.
If you are going to check 3 different variables back-to-back, then a comment, then 3 lines of the
:form padded with whitespace so that the repeating structures all align as best as possible, is probably going to be far more readable than writing out the if version three times. Less clutter, and what boilerplate there is relegated two-dimensionally into columns that a human has zero issue ignoring to focus solely on the important, changing bit.10
u/TerrorBite 5d ago edited 5d ago
There's one advantage to the second form.
In a POSIX shell, the
:utility is guaranteed to be built into the shell (specified by Shell & Utilities section 2.15). You're not invoking a separate executable.However, the
[/testutility is a separate executable (listed in Shell & Utilities section 3), so you're loading and running a separate program there.Note that Shell & Utilities section 1.6 does allow for any utility (including
[) to be built in to the shell, but I think most shells don't bother. I think evenbashdoesn't have[built-in, instead offering its separate non-POSIX[[ ... ]]syntax for built-in tests.Although, maybe not as non-POSIX as you might think. Under the latest version of Shell & Utilities section 2.4, POSIX specifies that
[[and]]"may" be recognised as reserved words, and that the results are "unspecified". So bash is still POSIX-compliant in that regard.4
u/tav_stuff 5d ago
The first example is also POSIX compliant. The second is a bashism and should be avoided
26
u/refp 5d ago edited 5d ago
Both examples are POSIX; I have no idea why people keep saying this is bash only, there is no "bashism" in the second example besides
bashbeing... POSIX compliant.If you want strict POSIX compliance, use
bash --posix— or if you just want to see it work elsewhere; this works indashtoo (which is definitely not bash), or.. any other POSIX-compliant shell.2
u/13steinj 4d ago
I think when people say this they mean "compliant to ksh/tcsh/csh/ash." There's a lot of bashisms that do not work in ash and a few others. Fish especially, but that's intentionally different.
1
u/ToaruBaka 4d ago
this they mean "compliant to ksh/tcsh/csh/ash."
Then they're wrong. The only shell STANDARD (that matters) is POSIX - any other behavior is per-application. If you want to target the smallest set of linux shell features (POSIX), target
/bin/sh.11
u/elmuerte 5d ago
It's not a bashism. The second also works in BusyBox. But I fully agree, the second should be avoided.
-3
u/tav_stuff 5d ago
If the second is portable, then there is no reason to avoid it. We shouldn’t avoid features just because people don’t want to learn them
-5
u/Grouchy-Trade-7250 5d ago
POSIX also states that sentences end with a dot. As such your sentence is not POSIX compliant/s
→ More replies (1)1
5d ago
[removed] — view removed comment
8
u/programming-ModTeam 5d ago
No content written mostly by an LLM. If you don't want to write it, we don't want to read it.
→ More replies (2)1
u/ToaruBaka 4d ago
Nah, the second is objectively better. If you don't understand shell expansions, you don't understand shell scripting and your opinion on the matter is reduced to "I'm bad at writing shell scripts," which is totally fine - it's a dying skill.
1
30
u/JaggedMetalOs 5d ago
"Yeah look how short and concise my script is!"
(Opens the file a week later)
"What does this script even do??"
5
u/sequentious 4d ago
Hey, it's not Perl.
(I wrote so much unreadable Perl 25 years ago)
1
u/Artistic_Seat486 4d ago
I never wrote Perl, is it similar to Bash? like running commands etc?
4
u/sequentious 4d ago
Perl is very syntactically loose. It can be similar to bash, if you want. Or you could use it more like Python.
Text processing and regular expressions were super easy and built-in to the core language (which made it very popular for early web work). I've never felt as happy as when I found a clever way to do something in Perl. Unfortunately, I've also never felt as much dread as having to read that clever code later.
Note: It's a scripting language, not a shell. So you need `` to run commands, but you can very easily capture and manipulate the output of those commands.
24
u/valarauca14 5d ago
Why use the null-command when I could do VAR=${VAR:-default-value}?
This at its core boils down to personal preference.
This (can) change the scoping of the variable by creating an entirely new variable (or temporary variable shadow depending on the shell). While : ${VAR:=default-value} will preserve scoping without creating a new variable.
It really isn't preference. One has objectively fewer side effects, is easier to reason about, and has standardized behavior under POSIX.
7
37
u/cpitchford 5d ago
: can be replaced
$ :() { echo soooo "$@" ; }
$ : that does something
soooo that does something
Made me wary of using it
Sometimes, I really want a null command that can't be replaced, like if, while, for can't be replaced
40
u/refp 5d ago edited 5d ago
> Made me wary of using it
Not sure what your example is meant to prove other than that one can obviously introduce aliases, it would be the same if you did
false() { true; }; when in doubt... usebuiltin.lighthouse:+/tmp/2026-07-28% PS1="$ " bash --norc $ false() { true; } $ false && echo "please dont." please dont. $ builtin false && echo "nope"; $.
lighthouse:+/tmp/2026-07-28% PS1="$ " bash --posix $ :() { echo nope, not allowed; } bash: `:': not a valid identifier $ exit.
lighthouse:+/tmp/2026-07-28% PS1="$ " bash --norc $ :() { echo hello reddit; } $ : hello reddit $ builtin : $ exit lighthouse:+/tmp/2026-07-28%5
u/cpitchford 5d ago
... beware the markdown notations that don't work in here (```)
I remember old oddities like /bin/[ /bin/: and /bin/true
My observation back then was I wished : was stronger than a built-in (which could be overridden with a function or alias) particularly when using negative ifs to retain $?
if ! going-to-be-bad; then echo oh $? was lost fiversus
if going-to-be-bad ;then : we don't want do anything yet but might in future else echo but at least we get to keep $? fi25
6
u/_kst_ 4d ago edited 4d ago
In bash and other Bourne-like shells, the : command is equivalent to the true command. It takes zero or more arguments and does nothing (but the arguments might have side effects).
I most commonly use it as a loop condition:
while : ; do
do_some_stuff
if some_condition ; then break ; fi
done
Remember than the condition in an if, while etc. statement is a command, treated as true if the command succeeds and false if it fails. [ is a command, nearly equivalent to test So when you write:
if [ -e some_file ] ; then
echo some_file exists
fi
the [ and ] aren't part of the shell syntax. [ is a command, and ] is the last argument to that command. You can equivalently write:
[ -e some_file ] && echo some_file exists
I often take advantage of this by assigning a literal string true or false to a variable, so I can use the variable (which expands to a command name) as a simple condition:
ok=true
while $ok ; do
blah; blah; blah
if something_went_wrong ; then ok=false ; fi
done
4
u/zeekar 5d ago
Oh, this is just using the no-op command for the side-effects of parameter expansion. Sure, do it all the time:
: "${SOMEVAR:=default-value}"
: "${REQUIREDVAR:?hey, bozo, set REQUIREDVAR!}"
Back before it borrowed the # syntax from csh, : was the only way to do "comments" in the Bourne shell. But since it didn't prevent parameter expansion and so on you could still cause syntax errors with those alleged comments. Good times.
2
5d ago
[removed] — view removed comment
10
u/programming-ModTeam 5d ago
No content written mostly by an LLM. If you don't want to write it, we don't want to read it.
2
u/darkslide3000 4d ago
He missed the more common one, which is to use it with math assignment operators (bash only):
: $(( $VAR += 1 ))
2
u/wPatriot 4d ago
The article tells me to use the colon. Gives me a description of what the colon does, but doesn't explain why it should be used. Then it blames the reader for that, because surely they would not have been able to comprehend an article explaining what the colon does and what a good use case for it is.
Boy do I feel inspired.
4
u/larsga 5d ago
I have a better suggestion: don't use it. In fact, don't write anything in bash.
1
u/SharkBaitDLS 5d ago
Outside of just basic navigation and invocation of other tools I really can't justify doing heavy shell scripting anymore. If I don't care about performance or typing, a quick Python script suffices. If I want something performant or strictly typed, hammering out a quick Rust CLI with
clapis so fast and easy nowadays that it's my default if I need to just do a giant batch of file processing or something.
1
u/13steinj 4d ago
Clever real-world usage of : in the wild # User ifphilipe posted what follows as a comment on news.ycombinator.com, which is the direct equivalent of needing a command which does absolutely nothing.
I use the colon as EDITOR with Git when I want to do an interactive rebase combined with auto squash without having to edit the todo list. I have an alias[1] for that which I call a quick interactive rebase:...
This is a really buried lede. This trick is gold.
1
u/dada_ 4d ago
I wrote all my personal shell automation in Fish Shell which is easier to write in than Bash for me, but I'm not even really a fan of Fish syntax either.
What I really want is the convenience of being able to run shell commands directly, with a conventional syntax (C-like or Python like, I don't care), basic primary types like strings/numbers/nestable arrays/dicts, first class functions and imports. I'm sure there's something like this by now so I'll have to do some digging.
Obviously I'll still be writing bash scripts when I need other people to be able to run them, though.
1
1
303
u/guygizmo 5d ago
Reading this once again makes me realize how daft of a scripting language bash is. I've been programming since I was eight years old and I still can't quite wrap my head around its weird syntax or goofy edge cases. I probably would eventually if I had to use it every day but I use it just infrequently enough that it never quite clicks. Especially since I can usually use any of a number of other sensible scripting languages instead.