Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

Consider

echo \ # this is a comment
foo

This gives:

$ sh foo.sh
 # this is a comment
foo.sh: line 2: foo: command not found

After some searching on the web, I found a solution by DigitalRoss on sister site Stack Overflow. So one can do

echo `: this is a comment` \
foo

or alternatively

echo $(: this is a comment) \
foo

However, DigitalRoss didn't explain why these solutions work. I'd appreciate an explanation. He replied with a comment "There used to be a shell goto command which branched to labels specified like : here. The goto is gone but you can still use the : whatever syntax ... : is a sort of parsed comment now.", but I'd like more details and context, including a discussion of portability.

Of course, if anyone has other solutions, that would be good too.

See also the earlier question How to comment multi-line commands in shell scripts?.

share|improve this question

2 Answers 2

up vote 14 down vote accepted

Comments end at the first newline (see shell token recognition rule 10), without allowing continuation lines, so this code has foo in a separate command line:

echo # this is a comment \
foo

As for your first proposal, the backslash isn't followed by a newline, you're just quoting the space: it's equivalent to

echo ' # this is a comment'
foo

$(: this is a comment) substitutes the output of the command : this is a comment. If the output of that command is empty, this is effectively a highly confusing way to insert a comment in the middle of a line.

There's no magic going on: : is an ordinary command, the colon utility, which does nothing. The colon utility is mostly useful when the shell syntax requires a command but you happen to have nothing to do.

# Sample code to compress files that don't look compressed
case "$1" in
  *.gz|*.tgz|*.bz2|*.zip|*.jar|*.od?) :;; # the file is already compressed
  *) bzip2 -9 "$1";;
esac

Another use case is an idiom for setting a variable if it's not already set.

: "${foo:=default value}"

The remark about goto is a historical one. The colon utility dates back from even before the Bourne shell, all the way to the Thompson shell, which had a goto instruction. The colon then meant a label; a colon is a fairly common syntax for goto labels (it's still present in sed).

share|improve this answer
    
Ok, I see. Are there any one better ways of inserting comments in this context that you are aware of? –  Faheem Mitha Aug 21 '11 at 16:05
3  
@FaheemMitha As recommended in the thread you cite: break up your command into manageable chunks and comment each chunk. If your command is so complicated as to need a comment in the middle, it's time to simplify it! –  Gilles Aug 21 '11 at 16:15
    
Well, the command in question has a lot of arguments... It's converting a bunch of video files into one file. I don't see a direct way to simplify it. Maybe create a list of some sort, and pass it as an argument? I guess that could be another question. –  Faheem Mitha Aug 21 '11 at 16:19
    
@FaheemMitha Example: make_FIND, a quickie script which builds a long list of arguments to find. Here, the motivation for building it chunk by chunk is that each chunk comes from the body of a loop, but the same style allows to comment on each chunk. –  Gilles Aug 21 '11 at 17:46
1  
Thanks for the example. My example is basically just a long list of names given as arguments to a command. I want comments attached to each name, because that is the most easy way for me to keep context. I don't see any obvious way to break the command up into pieces, and if I did, it might make it even longer, and it is long enough already. –  Faheem Mitha Aug 21 '11 at 18:42

You can achieve this using Bash arrays, e.g.

#!/bin/bash
CMD=(
  echo  # this is a comment
  foo
  )

"${CMD[@]}"

This defines an array, $CMD, and then expands it. Once expanded the resulting line is evaluated, so in this case echo foo is executed.

The text between ( and ) defines the array and is subject to the usual bash syntax, so everything on a line after # is ignored.

Note about preserving quoted whitespace

${CMD[@]} expands to a single string which is the concatenation of all the elements, separated by a space. Once expanded, Bash would then parse the string into tokens in the usual manner (c.f $IFS), which is often not what we want.

By contrast if the expansion is wrapped in double quotes, i.e. "${CMD[@]}", then each element in the array is preserved. Consider the difference between hello world second item and "hello world" "second item".

Illustrative example:

# LIST=("hello world" "second item")

# for ITEM in ${LIST[@]}; do echo $ITEM; done
hello
world
second
item

# for ITEM in "${LIST[@]}"; do echo $ITEM; done
hello world
second item
share|improve this answer
    
Thank you for the answer, @RobM. So, your suggestion is to include the comments/meta-information about the items in the list in the bash array itself? This question would probably be more useful if I had included an example of the kind of command I was trying to use. I don't know why I didn't. –  Faheem Mitha Apr 5 at 16:50
    
Bah, it turns out I didn't read your question carefully. Thought you were asking the question you linked to. Oops. :) –  RobM Apr 10 at 17:32
    
${CMD[@]} does not expand to a single string. – It expands to many strings: Not just split for each element of the array, but also on whitespace in each element. (I.e.: completely useless) –  Robert Siemer yesterday

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.