Well that's exciting. I learned a lot of uses for ":" today.
However, the only one I already knew...
if some-command; then
: # command required
else
echo "command failed"
fi
I used to do that until I learned of if ! some-command; then
echo "command failed"
fi
It's in the POSIX standard so it's not just a bashism: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V...> If the pipeline does not begin with the "!" reserved word, the exit status shall be the exit status of the last command specified in the pipeline. Otherwise, the exit status shall be the logical NOT of the exit status of the last command
I once created a set of shell functions which I wanted to have a docstring-like functionality. The solution I came up with was to have each shell function start with the : command with a string as an argument. Since : is an actual command, not a comment, it was preserved as part of the function, and could be extracted at runtime using relatively simple parsing to do introspection.
Example:
foo(){
: "This is a docstring for the foo() function"
bar --verbose | baz --quiet
}
(Repost of <https://news.ycombinator.com/item?id=29152308>)I am not a huge fan of most of these, but a few do seem useful.
: "${1:?missing argument, aborting!}"
I wouldn't use this because I would want to give $1 a name for the rest of the script, so I would assign. But it can be a nice way to give a clear error for missing required environment variables.Many of the others (like truncating files) are probably more clearly written with dedicated commands, but may come in useful if you are going to extreme lengths to avoid dependencies outside of the shell.
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:
riq = -c sequence.editor=: rebase --interactive
[1]: https://github.com/fphilipe/dotfiles/blob/94f2ff70bade070694...I actually did absolutely need it recently when I golfed together a shell script that is simultaneously a valid YAML file [0]. Sometimes having no-op tokens is nice!
My personal favourite use of the colon command is what I've written about some years ago: https://johannes.truschnigg.info/writing/2021-12_colodebug/
Why take a perfectly readable if-statement and turn it into something, 99.9% of people would need to lookup. Concise != better. You can make it one line with:
[ -z "$1" ] && { echo "missing argument, aborting." 1>&2; exit 1 }Whats the advantage of the colon for the truncation example
>file1 >file2
versus : >file1 >file2
I've done quick and dirty, interactive truncation like the former for many years, no colon. But I would not use it in scriptsAccording to https://www.in-ulm.de/~mascheck/bourne/ SVR4 (1989) had a bug when using this method in a for or while loop and this bug showed up in a SunOS 5 variant, too
Apparently, early in the shell's evolution, : was used as a comment marker before # was added
Single quotes could be used to prevent undesired behaviour
: echo output 1>&2
: `echo output 1>&2`
: '`echo output 1>&2`'
System III (1981) also had a bug when using : as a substitute for true if false;then :;fi
returned 1 instead of 0This "hidden knowledge" is fun to read but pain to remember and use, especially when working with multi-platform environments //
Switched from bash to plain python scripts for shell stuff everywhere several years ago, and never looked back into bash zoo anymore. Stable syntax across Win/Mac/Linux, no bash/zsh/msys2 obscure differences, normal errors and Clause writes scaffolds quick and flawless anyway
Off-topic, but I am reminded of Larry's First and Second Laws of Language Redesign (which Larry Wall discovered/stated when he designed Perl 6, which is now Raku):
1. Everyone wants the colon.
2. Larry gets the colon.Nice one! I love those weird bash tricks.
Some of the examples here are interesting, but they show parameter substitution more than colon itself: https://tldp.org/LDP/abs/html/parameter-substitution.html
In small scopes, I tend to inline the `:?` validation inside the arg of the command. `echo "${1:? first param required}"`
Another usecase is to use colon in the body of a while loop, while doing work in the condition of the loop.
while rlwrap -o -S'>> ' tr a-z A-Z ; do :; done
Gives you the "do X while it succeeds. stop when it returns non-0" semantics.I've also written about this and other bash tricks over the years in https://github.com/kidd/scripting-field-guide/blob/master/bo.... You might like them :)
I have probably the worst use case, but I like it. I have a very specifically structured ZDOTDIR, and I write everything in a way that is self-documenting.
After a while, you probably know more commands and utilities than you know what to do with, and you'll forget they exist when you need them. In order to not waste time looking for a program for a particular and infrequent purpose, I create "do nothing" aliases like `: alias f3probe` so I can realize I just forgot I already have something for it. Nice predictable pattern to grep with `^: alias` to look through all of these.
life is way too short to deal with this nightmare of a language and its 50000 footguns for anything longer than a 2 line script, especially in the age of LLMs. Just write a python/TS/any real language script instead. Bash is great for the command line, it should be limited to use there.
What if there was a less cryptic way to have mandatory arguments, something harder to get wrong, like
param(
[parameter(mandatory)] $name
)
"Hello, $name!"
And then: $ ./script.ps1 Dave
Hello, Dave!
$ ./script.ps1 -name Dave
Hello, Dave!
$ ./script.ps1
cmdlet script.ps1 at command pipeline position 1
Supply values for the following parameters:
name: <cursor here>
Or non interactively: $ pwsh -nonint ./script.ps1
script.ps1: Cannot process command because of one or more missing mandatory parameters: name.I always wanted to learn more about scripting. But today I am not as passionate as before because LLMs write working scripts most of the time. I am wondering if it is true for most programming techniques and quirks. Are we going to write code to solve low level problems?
The other day there was a blog post about learning SIMD. I think in future "programmers" will just nag about the speed of the program and the coding assistant will eventually introduce SIMD to the source code.
It is a little sad but we have to go with the current if we want to survive.
One of the things I do with : is infinite while-loops, like:
while :; do
<do stuff>
sleep n
done
Maybe to mainstream to make the cut!?Good to know, but looks less readable than `if` example.
I often use : to set default values of configuration envs. e.g, in my dotfiles bootstrap script I have:
: "${DOTFILES_PATH:=$HOME/.dotfiles}"
Which will use $DOTFILES_PATH value if it's set, otherwise it's going to be $HOME/.dotfiles> ( : < dataset.json ) && echo YES # is dataset.json readable?
The subshell execution parentheses and the colon are superfluous here, just:
< dataset.json && echo YES
Redirections do not require a colon command to hang off of, and there is no need to fork a subshell to execute such a command.> ( : >> result.json ) && echo YES # is result.json writable?
As a go-to idiom for a writability test, it gives me pause. If the file didn't exist, we created a zero-length one. That might be okay if we are going to write to it anyway as the next action.
If we are testing because we intend to overwrite it, why not just "> result.json" (which is by itself an idiom for truncating a file to zero length).
When would we every do this? Maybe before some command which takes the file name as a destination file argument rather than using output redirection, and which performs a lengthy computation before trying to open the file for writing. We can catch the permission error early.
I don't think I've ever coded such a test; normally you just do the operation that writes to the file and let that fail.
In POSIX C, there is a function access() for doing these kinds of tests. But it has a special purpose: it is meant to be used by a setuid root process to perform a permission test as if it were the real user/group (the one which elevated privilege to root). I.e. it's not can we do this operation, but should we do this operation (would we still be allowed, if we dropped privileges back to the original user).
the colon does nothing, which makes it the only bash command an LLM can't over-engineer
there are some early unix tapes floating around, and in those early shells, i'm fairly certain the colon was one of only two special-cased code paths after the command line was parsed. does anyone recall more specifically?
I really hate bash because of its unreadable syntax, and this does not help it in any way.
We have some large bash scripts in my company, ~10,000 LOC spread across multiple files, all sourcing each other and what not. It is truly hard to read bash, which means it is truly hard to maintain bash, which means that when the one person knowing the bash scripts in your company goes away, you're in for some "fun".
My point is, these quirks are not useful, except for some bash enthusiasts.
I've read through all of the examples in the article & they all seem to serve to sole purpose of turning readable multiple line code into one-liners.
One-liners are a cool little artifact of early shell culture & are sometimes still useful today if they're short to avoid the readability problems of `/` when copy pasting a quick shell command to run, but they have no place in scripts.
None of this seems useful to me.
> if you are like me and prefer less typing (gotta go fast)
Yeah, no.
Here's another tip I picked up last millenium: If you're using a GUI and like decorating your shell prompt with information, format it like this:
: stuff;
Then you can copy/paste entire lines of commands.(Yes, this assumes you don't put naughty fragile stuff in your prompt. Buy you're smart enough not to do that.)
I usually skip the get option builtin, and use : as...
while : ; do case "$1" in "") break;; -f|-foo) shift; whatever;; *) usage; exit 1;; esac done
For this... instead
if something; then
true
else
echo ERROR
exit 1
fi
Using : would be too much here.For anything else including json etc. I usually go to duckdb. Awesome support, single file install, readable, easy to maintain.
Powershell on Linux or Unix? Just another huge dependency if you manage 1000s of machines, and good luck finding a Linux gal/guy wanting or able to touch pwsh without chemical grade gloves.
Thank you for this.
Also, I'll never use it.
Because a language feature that needs marketing is against readability, among those in my target audience who have not yet read the marketing.
I need my shell scripts to be long enough to explain to my audience exactly what they are doing.
> Though.. what if I told you the above four lines could be replaced by just... one?
I'd reject the pull request. Bash is already bad as programming language (the goodness of language for long code is inversely proportional to how nice it is for shell one-liners), this is just turning "bad" into "line noise"
If your bash script takes more than one screen, rewrite it in Python, hell, rewrite it in Perl, even that's better
ensure true
It's almost but not quite in the article. And you don't necessarily always want this. It depends if you want else-cmds to run only when condition fails, or when either condition or then-cmds fails.
You could write the word true instead of :, coincidentally showing that : never really was a no-op in the first place. It's so not-no-op that there is even an entire external executable /bin/true to do the same job.
condition && {
then-cmds # might exit > 0
: # ensure this block ends true
} || {
else-cmds
}the truncation is a poor example.
- it creates the file if it does not exist, not merely truncate. as a tutorial kind of blog post this incomplete description matters IMO.
- it would work the same without the colon (similar for default variable assignment examples). we generally strive not to have "extra" things, like useless use of cat.
- educationally it's useful to demonstrate that redirection, like parameter expansion, works before the command executes (the null command in this case), but the article doesn't explain that at all!
otherwise i <3 this article. some uses of colon i had never thought of or seen before. like file truncation, not sure i'd use them but it was cool to see them.
Prefer
if x then :; else something; fi
over if ! x; then something; fi
Really? Colon is the appendix of the shell.For decades I used bash and never knew and saw this syntax. And recently discovered that by a code generated by Claude.
And now I see this article. So I guess that it is a construct suddenly popularized by llm.
Cool!
Btw, can the sequence :? be called "reverse Elvis" ?
Maybe it's just the fact that I woke up 10 minutes ago, but the readability of this looks awful. Even more than just usual shell scripts.
interesting
Another fun use of colon:
:(){ :|:& };:
I've never liked cleverness in scripting when clarity costs effectively nothing.
This is one of those clever things, similar to people using perl5 use trinary expressions. Like, are you TRYING to make this obtuse and hard to read?
This is an excellent article that helps people decide against writing shell scripts. I abandoned doing so shortly after I switched to linux. Since then I was also using ruby. I still do not understand why people would prefer shell scripts over ruby (or python). On systems without ruby or python, one may see a benefit in using shell scripts; other than that I fail to see why shell scripts are necessary.
Shell scripts simply suck for many reason. They are ugly, verbose, convoluted, outright stupid too such as argument passing into functions. Then there is straight up retarded stuff such as case/esac. Whoever came up with that was clearly an incompetent language designer.
[flagged]
[dead]
[dead]
[flagged]
Articles like this are fun but they all come from posix shell syntax being fundamentally bad for scripting/programming. All the piping stuff is great, of course. And the overall ecosystem is great. But the interpretation of the script itself working by a series of string substitutions is a mechanism we wouldn't accept in a regular programming language. And there's no excuse for it really, except that shell syntax is really, really old.
For example, what does `$foo` mean in shell syntax? In any reasonable language (perl or powershell, for example, or python if you drop the `$`), it's an expression that evaluates to whatever value's inside that variable. In shell, `$foo` isn't an expression in that sense, and what it does depends on what's inside it via a variety of string substitution rules.
This is the main reason we have arcane articles like this.
That said, nice article.