What is the use case of noop [:] in bash?

BashShellNoop

Bash Problem Overview


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.

Bash Solutions


Solution 1 - Bash

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

Solution 2 - Bash

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

Solution 3 - Bash

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

Solution 4 - Bash

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  

                          

Solution 5 - Bash

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

Solution 6 - Bash

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.

Solution 7 - Bash

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

Solution 8 - Bash

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

Solution 9 - Bash

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.

Solution 10 - Bash

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

Solution 11 - Bash

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


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

Solution 12 - Bash

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

Solution 13 - Bash

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMandar PandeView Question on Stackoverflow
Solution 1 - BashGilles 'SO- stop being evil'View Answer on Stackoverflow
Solution 2 - BashStephen OstermillerView Answer on Stackoverflow
Solution 3 - BashChris PfohlView Answer on Stackoverflow
Solution 4 - BashMarkView Answer on Stackoverflow
Solution 5 - BashUlysse BNView Answer on Stackoverflow
Solution 6 - BashGeoffrey RitcheyView Answer on Stackoverflow
Solution 7 - BashBitdiotView Answer on Stackoverflow
Solution 8 - BashsphakkaView Answer on Stackoverflow
Solution 9 - BashMaëlanView Answer on Stackoverflow
Solution 10 - BashekkisView Answer on Stackoverflow
Solution 11 - BashBrendan AbelView Answer on Stackoverflow
Solution 12 - BashJulioView Answer on Stackoverflow
Solution 13 - BashamrxView Answer on Stackoverflow