A shell colon does nothing. Use it anyway

refp.se

363 points by olexsmir a day ago


garethrowlands - 10 hours ago

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.

amiga386 - 11 hours ago

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

teddyh - 8 hours ago

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>)
kevincox - a day ago

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.

fphilipe - 11 hours ago

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...
dominiwe - 10 hours ago

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!

[0]: https://domi.work/blog/posts/compose_polyglot/

c0l0 - 12 hours ago

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/

archargelod - 11 hours ago

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 }
1vuio0pswjnm7 - 3 hours ago

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 scripts

According 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 0
search_facility - 10 hours ago

This "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

gpvos - 11 hours ago

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.
rgrau - 11 hours ago

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 :)

ocd - 10 hours ago

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.

zaptheimpaler - 15 hours ago

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.

IdiotSavage - 12 hours ago

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.
darkoob12 - 8 hours ago

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.

mandus - 5 hours ago

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!?
jagadaga - 14 hours ago

Good to know, but looks less readable than `if` example.

olexsmir - 11 hours ago

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
kazinator - 14 hours ago

> ( : < 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).

luciana1u - 12 hours ago

the colon does nothing, which makes it the only bash command an LLM can't over-engineer

mqus - 8 hours ago

Is : the same as `true` ?

__del__ - 10 hours ago

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?

orphereus - 13 hours ago

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.

lucideer - 13 hours ago

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.

kps - 7 hours ago

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.)

m2f2 - 12 hours ago

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.

jeffrallen - 12 hours ago

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.

PunchyHamster - 14 hours ago

> 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

Brian_K_White - 3 hours ago

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
    }
jiveturkey - a day ago

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.

normie3000 - 15 hours ago

Prefer

  if x then :; else something; fi
over

  if ! x; then something; fi
Really? Colon is the appendix of the shell.
greatgib - 10 hours ago

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.

kunley - 11 hours ago

Cool!

Btw, can the sequence :? be called "reverse Elvis" ?

Hamuko - 14 hours ago

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.

caruasdo - 8 hours ago

interesting

sgarland - 5 hours ago

Another fun use of colon:

:(){ :|:& };:

nekusar - 9 hours ago

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?

shevy-java - 13 hours ago

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.

fenestella - 8 hours ago

[flagged]

vladsiu - 13 hours ago

[dead]

draw_down - 8 hours ago

[dead]

dfasifsaf - 6 hours ago

[flagged]