180

I searched for noop in bash (:), but was not able to find any good information. What is the exact purpose or use case of this operator?

I tried following and it's working like this for me:

[mandy@root]$ a=11
[mandy@root]$ b=20
[mandy@root]$ c=30
[mandy@root]$ echo $a; : echo $b ; echo $c
10
30

Please let me know, any use case of this operator in real time or any place where it is mandatory to use it.

tshepang
  • 12,111
  • 21
  • 91
  • 136
Mandar Pande
  • 12,250
  • 16
  • 45
  • 72

14 Answers14

249

It's there more for historical reasons. The colon builtin : is exactly equivalent to true. It's traditional to use true when the return value is important, for example in an infinite loop:

while true; do
  echo 'Going on forever'
done

It's traditional to use : when the shell syntax requires a command but you have nothing to do.

while keep_waiting; do
  : # busy-wait
done

The : builtin dates all the way back to the Thompson shell, it was present in Unix v6. : was a label indicator for the Thompson shell's goto statement. The label could be any text, so : doubled up as a comment indicator (if there is no goto comment, then : comment is effectively a comment). The Bourne shell didn't have goto but kept :.

A common idiom that uses : is : ${var=VALUE}, which sets var to VALUE if it was unset and does nothing if var was already set. This construct only exists in the form of a variable substitution, and this variable substitution needs to be part of a command somehow: a no-op command serves nicely.

See also What purpose does the colon builtin serve?.

Community
  • 1
  • 1
Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
  • 14
    Good summary. Also, the original Bourne shell didn't use `#` for comments (or `#!/bin/sh` shebangs); the `:` command introduced comments, and woe betide the naïve programmer who made a nice box of stars in their comments: `: ****` (or, worse, `: * * *`). – Jonathan Leffler Sep 13 '12 at 13:26
  • 3
    @JonathanLeffler: Shame on me, but I don't get it. What would happen to `: * * *`? – DevSolar Nov 05 '14 at 14:23
  • 11
    Because `:` is a command, the shell still has to process its arguments before it can discover that `:` ignores them. Mostly, you are just making the shell do extra work in expanding `*` to a list of files in the current directory; it won't actually affect how the script works. – chepner Nov 05 '14 at 14:48
  • I suppose it might make your script fail if you happen to run in in a directory with a lot of files, making the glob expansion go over the command length limit? Maybe only if you have `set -e` on. Anyway, hopefully we can all just agree to use `#` :) – Jack O'Connor Nov 07 '15 at 22:14
  • 2
    I sometimes use `while : ; do ... ; done` if I want a quick and dirty infinite loop. (Usually there's a `sleep` in the loop, and I type Ctrl-C to kill it.) – Keith Thompson May 12 '16 at 01:05
  • I can't believe no one has said: `while : ; do : ; done || :` – Ярослав Рахматуллин Apr 07 '22 at 17:18
41

I use it for if statements when I comment out all the code. For example you have a test:

if [ "$foo" != "1" ]
then
    echo Success
fi

but you want to temporarily comment out everything contained within:

if [ "$foo" != "1" ]
then
    #echo Success
fi

Which causes bash to give a syntax error:

line 4: syntax error near unexpected token `fi'
line 4: `fi'

Bash can't have empty blocks (WTF). So you add a no-op:

if [ "$foo" != "1" ]
then
    #echo Success
    :
fi

or you can use the no-op to comment out the lines:

if [ "$foo" != "1" ]
then
    : echo Success
fi
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
18

If you use set -e, then || : is a great way to not exit the script if a failure happens (it explicitly makes it pass).

Melebius
  • 6,183
  • 4
  • 39
  • 52
Chris Pfohl
  • 18,220
  • 9
  • 68
  • 111
  • 2
    I've found this useful for certain shell-script-calling applications. For example, Automator on Mac will exit on error and not tell you why. But using `|| :` then later handling the error yourself with an `exit 0` will let you display all stdout and stderr to the app window during debugging. Consider `{ foo ... 2>&1; } || :` which is much simpler than setting up more complex traps and if-thens. – Beejor Dec 08 '17 at 20:54
9

Ignoring alias arguments

Some times you want to have an alias that doesn't take any argument. You can do it using ::

> alias alert_with_args='echo hello there'

> alias alert='echo hello there;:'

> alert_with_args blabla
hello there blabla

> alert blabla
hello there
Ulysse BN
  • 10,116
  • 7
  • 54
  • 82
  • not the downvoter, but this answer doesnt demonstrate how `:` works, unfortunately. The example you provided seems redundant. – qodeninja Jul 20 '17 at 19:28
  • 3
    The question is not how it works, but _what is the use case_. It's true my answer is a bit similar to https://stackoverflow.com/a/37170755/6320039. But It is really not the same use case. I'd be glad to improve my answer but I really don't krnow how here.. – Ulysse BN Jul 20 '17 at 19:53
  • 1
    This is a bit funky- a corner case, if you will- but still quite interesting and a could be a clever trick to have in one's back pocket. – Mike S Nov 08 '17 at 23:36
  • 2
    The same can be achieved with `#` – Zoey Hewll Jun 18 '18 at 06:51
  • 3
    @ZoeyHewll, not quite. As aliases are substituted *textually* before any kind of execution, using an alias to `#` would make everything that comes syntactically after on the same line to be ignored. This is really different (consider `my_alias arg ; other_command` or, conversely, `my_alias arg1 {BACKSLASH}{NEWLINE} arg2`), and is probably not what you want as it may cause syntax errors (guess what `while my_alias ; do true ; done` or `while true ; do my_alias ; done` will do). – Maëlan Aug 25 '19 at 00:16
  • @Maëlan true, I hadn't considered the different ways `:` and `#` deal with subsequent commands – Zoey Hewll Aug 26 '19 at 03:14
  • 1
    @ZoeyHewll, not quite #2. When the shell is run with `set -x`, one would never see the stuff after `#` – Ярослав Рахматуллин Apr 07 '22 at 17:14
8

You would use : to supply a command that succeeds but doesn't do anything. In this example the "verbosity" command is turned off by default, by setting it to :. The 'v' option turns it on.

#!/bin/sh
# example
verbosity=:                         
while getopts v OPT ; do          
   case $OPT in                  
       v)        
           verbosity=/bin/realpath 
       ;;
       *)
           exit "Cancelled"
       ;;             
   esac                          
done                              

# `$verbosity` always succeeds by default, but does nothing.                              
for i in * ; do                   
  echo $i $($verbosity $i)         
done                              

$ example
   file

$ example -v
   file /home/me/file  
Mark
  • 81
  • 1
  • 2
  • strictly speaking, assigning a value to a variable is "doing something", hence why it doesn't throw an error in your case statement. – qodeninja Jul 20 '17 at 19:30
  • 1
    @qodeninja: Nobody claimed that the variable assignment "did nothing"; the point is that the `$(verbosity $i)` later (and conditionally) does nothing. – Lightness Races in Orbit Oct 23 '17 at 14:26
8

One use is as multiline comments, or to comment out part of your code for testing purposes by using it in conjunction with a here file.

: << 'EOF'

This part of the script is a commented out

EOF

Don't forget to use quotes around EOF so that any code inside doesn't get evaluated, like $(foo). It also might be worth using an intuitive terminator name like NOTES, SCRATCHPAD, or TODO.

Beejor
  • 8,606
  • 1
  • 41
  • 31
  • Cool trick! Especially for scratch notes and code requiring dozens of "#"s when hard-wrapped. A side effect is that unlike comment lines, some syntax highlighters will ignore heredocs, coloring them like normal code (or at least plain text). Which can be great for examining commented-out code, or saving your eyes from paragraphs in a neon comment color. Only caveat is that such blocks should be noted as being comments in shared code, since the syntax is unique and could cause confusion. Then again, this is shell we're talking about, where potential for confusion is systemic. – Beejor Dec 08 '17 at 21:29
  • I was expecting this to be a thing people don't like for some reason (confusion). Nice to see some upvotes :) – Ярослав Рахматуллин Apr 07 '22 at 16:58
3

Sometimes no-op clauses can make your code more readable.

That can be a matter of opinion, but here's an example. Let's suppose you've created a function that works by taking two unix paths. It calculates the 'change path' needed to cd from one path to another. You place a restriction on your function that the paths must both start with a '/' OR both must not.

function chgpath() {
    # toC, fromC are the first characters of the argument paths.
    if [[ "$toC" == / && "$fromC" == / ]] || [[ "$toC" != / && "$fromC" != / ]]
    then
        true      # continue with function
    else
        return 1  # Skip function.
    fi

Some developers will want to remove the no-op but that would mean negating the conditional:

function chgpath() {
    # toC, fromC are the first characters of the argument paths.
    if [[ "$toC" != / || "$fromC" == / ]] && [[ "$toC" == / || "$fromC" != / ]]
    then
        return 1  # Skip function.
    fi

Now -in my opinion- its not so clear from the if-clause the conditions in which you'd want to skip doing the function. To eliminate the no-op and do it clearly, you would want to move the if-clause out of the function:

    if [[ "$toC" == / && "$fromC" == / ]] || [[ "$toC" != / && "$fromC" != / ]]
    then
        cdPath=$(chgPath pathA pathB)   # (we moved the conditional outside)

That looks better, but many times we can't do this; we want the check to be done inside the function.

So how often does this happen? Not very often. Maybe once or twice a year. It happens often enough, that you should be aware of it. I don't shy away from using it when I think it improves the readability of my code (regardless of the language).

HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
Bitdiot
  • 1,506
  • 2
  • 16
  • 30
  • 3
    If you are answering a question about the purpose of `:`, you should use `:`, not `true`, in the answer. That said, the easiest way to negate the conditional here is to use one `[[ ... ]]` command and prefix it with `!`: `if ! [[ ( ... && ... ) || ( ... && ... ) ]]; then`. – chepner Nov 05 '14 at 14:54
  • 1
    Ah, very nice, and I should down vote my answer. Hmmm.... I'm still thinking of examples where I had to use a no-op clause. This is the best I came up with. – Bitdiot Nov 05 '14 at 14:58
  • It's really just retained for backwards compatibility, although `: ${...=...}` is arguably less awkward-looking than `true ${...=...}`. Some might prefer `while :; do` to `while true; do` for terseness as well. – chepner Nov 05 '14 at 15:08
3

Two of mine.

Embed POD comments

A quite funky application of : is for embedding POD comments in bash scripts, so that man pages can be quickly generated. Of course, one would eventually rewrite the whole script in Perl ;-)

Run-time function binding

This is a sort of code pattern for binding functions at run-time. F.i., have a debugging function to do something only if a certain flag is set:

#!/bin/bash
# noop-demo.sh 
shopt -s expand_aliases

dbg=${DBG:-''}

function _log_dbg {
    echo >&2 "[DBG] $@"
}

log_dbg_hook=':'

[ "$dbg" ] && log_dbg_hook='_log_dbg'

alias log_dbg=$log_dbg_hook


echo "Testing noop alias..."
log_dbg 'foo' 'bar'

You get:

$ ./noop-demo.sh 
Testing noop alias...
$ DBG=1 ./noop-demo.sh 
Testing noop alias...
[DBG] foo bar
sphakka
  • 457
  • 4
  • 11
3

Somewhat related to this answer, I find this no-op rather convenient to hack polyglot scripts. For example, here is a valid comment both for bash and for vimscript:

":" #    this is a comment
":" #    in bash, ‘:’ is a no-op and ‘#’ starts a comment line
":" #    in vimscript, ‘"’ starts a comment line

Sure, we may have used true just as well, but : being a punctuation sign and not an irrelevant English word makes it clear that it is a syntax token.


As for why would someone do such a tricky thing as writing a polyglot script (besides it being cool): it proves helpful in situations where we would normally write several script files in several different languages, with file X referring to file Y.

In such a situation, combining both scripts in a single, polyglot file avoids any work in X for determining the path to Y (it is simply "$0"). More importantly, it makes it more convenient to move around or distribute the program.

  • A common example. There is a well-known, long-standing issue with shebangs: most systems (including Linux and Cygwin) allow only one argument to be passed to the interpreter. The following shebang:

    #!/usr/bin/env interpreter --load-libA --load-libB
    

    will fire the following command:

    /usr/bin/env "interpreter --load-libA --load-libB" "/path/to/script"
    

    and not the intended:

    /usr/bin/env interpreter --load-libA --load-libB "/path/to/script"
    

    Thus, you would end up writing a wrapper script, such as:

    #!/usr/bin/env sh
    /usr/bin/env interpreter --load-libA --load-libB "/path/to/script"
    

    This is where polyglossia enters the stage.

  • A more specific example. I once wrote a bash script which, among other things, invoked Vim. I needed to give Vim additional setup, which could be done with the option --cmd "arbitrary vimscript command here". However, that setup was substantial, so that inlining it in a string would have been terrible (if ever possible). Hence, a better solution was to write it in extenso in some configuration file, then make Vim read that file with -S "/path/to/file". Hence I ended up with a polyglot bash/vimscript file.

Maëlan
  • 3,586
  • 1
  • 15
  • 35
3

suppose you have a command you wish to chain to the success of another:

cmd="some command..."
$cmd
[ $? -eq 0 ] && some-other-command

but now you want to execute the commands conditionally and you want to show the commands that would be executed (dry-run):

cmd="some command..."
[ ! -z "$DEBUG" ] && echo $cmd
[ -z "$NOEXEC" ] && $cmd
[ $? -eq 0 ] && {
    cmd="some-other-command"
    [ ! -z "$DEBUG" ] && echo $cmd
    [ -z "$NOEXEC" ] && $cmd
}

so if you set DEBUG and NOEXEC, the second command never shows up. this is because the first command never executes (because NOEXEC is not empty) but the evaluation of that fact leaves you with a return of 1, which means the subordinate command never executes (but you want it to because it's a dry run). so to fix this you can reset the exit value left on the stack with a noop:

[ -z "$NOEXEC" ] && $cmd || :
ekkis
  • 9,804
  • 13
  • 55
  • 105
3

I've also used in it scripts to define default variables.


: ${VARIABLE1:=my_default_value}
: ${VARIABLE2:=other_default_value}
call-my-script ${VARIABLE1} ${VARIABLE2}

Brendan Abel
  • 35,343
  • 14
  • 88
  • 118
  • What's the benefit of this over `VARIABLE1=${VARIABLE1:-my_default_value}`? Just that it's shorter, or is there something else? – mattmc3 Dec 17 '21 at 14:50
2

null command [:] is actually considered a synonym for the shell builtin true. The ":" command is itself a Bash builtin, and its exit status is true (0). ` $ : $ echo $? # 0

while :
do
   operation-1
   operation-2
   ...
   operation-n
done

# Same as:
    while true
    do
      ...
    done

Placeholder in if/then test:

if condition
then :   # Do nothing and branch ahead
else     # Or else ...
   take-some-action
fi


$ : ${username=`whoami`}
$ ${username=`whoami`}   #Gives an error without the leading :

Source: TLDP

amrx
  • 282
  • 4
  • 19
1

I sometimes use it on Docker files to keep RUN commands aligned, as in:

RUN : \
    && somecommand1 \
    && somecommand2 \
    && somecommand3

For me, it reads better than:

RUN somecommand1 \
    && somecommand2 \
    && somecommand3

But this is just a matter of preference, of course

Julio
  • 5,208
  • 1
  • 13
  • 42
0

I used the noop today when I had to create a mock sleep function to use in bats testing framework. This allowed me to create an empty function with no side effects:

function sleep() {
  :
}
thoredge
  • 12,237
  • 1
  • 40
  • 55