Solved Use of : in sh scripts

I have seen things like the following in various scripts:
Code:
: ${GNUBIN=/usr/local/gnu/bin}
What is the purpose of the colon in that example? And why enclose the assignment with ${} instead of simply
Code:
GNUBIN=/usr/local/gnu/bin

This particular example is from /usr/local/share/gnulib/config/srclistvars.sh, but I think the first place I saw this use of colons was in some of the poudriere scripts.

Thanks,

Denver
 
Code:
$ unset GNUBIN
$ : ${GNUBIN=/usr/local/gnu/bin}
$ echo $GNUBIN
/usr/local/gnu/bin
$ GNUBIN=blather   
$ : ${GNUBIN=/usr/local/gnu/bin}
$ echo $GNUBIN
blather
$ ${GNUBIN=/usr/local/gnu/bin}
/bin/sh: blather: not found
 
Found a better explanation here

Basically, the shell would try to execute /usr/local/gnu/bin if the colon wasn't there.
 
Thanks Jose & gpw928. For special assignments using ${}, the man page lists :-, :=, :?, and :+. No mention of a bare = inside ${} that I could find.

I also read the same thing, that the colon is "A null command that returns a 0 (true) exit value". So why preface special assignments with a colon so often? If it doesn't do anything, then I wouldn't expect to see it as often as I do. I also seem to remember reading a reply somewhere in this forum, where the author advises the reader not to forget the leading colon. But I can't find it right now.

I did some of the same experiments with assignments and echo's, but I hadn't quite figured out why the "not found" error was happening.

Here's the explanation: The ${} assignment returns the value of the assignment, which I wasn't aware of. And the leading : command doesn't just return true, it also eats any parameters, which prevents "not found" errors, or worse. I wasn't aware of that aspect of it either.

Thanks,

Denver
 
Thanks. For special assignments using ${}, the man page lists :-, :=, :?, and :+. No mention of a bare = inside ${} that I could find.
sh(1)
"In the parameter expansions shown previously, use of the colon in the format results in a test for a parameter that is unset or null; omission of the colon results in a test for a parameter that is only unset."
So ${foo=bar} will set foo to bar only if foo is unset, whereas ${foo:=bar} will set foo to bar if foo is unset or null.
 
<snip>... why preface special assignments with a colon so often? If it doesn't do anything, then I wouldn't expect to see it as often as I do. I also seem to remember reading a reply somewhere in this forum, where the author advises the reader not to forget the leading colon. But I can't find it right now.
<snip>
If /bin/sh had an isset keyword like some languages do, then the following pseudocode would basically accomplish the same thing as the expression : ${GNUBIN=/usr/local/gnu/bin}
Code:
if [ isset GNUBIN ]; then GNUBIN=/usr/local/gnu/bin; fi

However, /bin/sh does not have an isset keyword that I know of, and this, I suspect, may be one of the reasons why we see the colon operator as often as we do. I know of no other way to accomplish an "isset()" type of test on an environment variable without it. Somewhat cryptic perhaps, but that's just the nature of POSIX and shell languages.
 
To add to the excellent earlier comments, another way of thinking about
: ${GNUBIN=/usr/local/gnu/bin}
is that it is a shorter (and less redundant) way to do
GNUBIN=${GNUBIN-/usr/local/gnu/bin}
 
That makes two things that I don't see documented in sh(1):

Code:
: ${FOO=foo} # bare = inside {}
FOO=${FOO-foo} # bare - inside {}

So, a few questions:

1. Are these documented anywhere?
2. What is the difference between - and = as in FOO=${FOO=foo} and FOO=${FOO=foo} They seem to be the same to me?

I'm trying to understand the logic of ${FOO=foo} only assigning if FOO is not already set... and without any documentation (that I can find), the only thing I can come up with is "just remember it."
 
From the manual for sh(1):
Code:
     ${parameter:-word}
             Use Default Values.  If parameter is unset or null, the expansion
             of word is substituted; otherwise, the value of parameter is
             substituted.

     ${parameter:=word}
             Assign Default Values.  If parameter is unset or null, the
             expansion of word is assigned to parameter.  In all cases, the
             final value of parameter is substituted.  Quoting inside word
             does not prevent field splitting or pathname expansion.  Only
             variables, not positional parameters or special parameters, can
             be assigned in this way.
     ...
     In the parameter expansions shown previously, use of the colon in the
     format results in a test for a parameter that is unset or null; omission
     of the colon results in a test for a parameter that is only unset.
The same manual page says of the null command:
Code:
     :       A null command that returns a 0 (true) exit value.
However bash(1) has a much more explicit description:
Code:
       : [arguments]
              No effect; the command does nothing beyond expanding arguments
              and performing any specified redirections.  The return status is
              zero.
 
Those docs you cite are different from what I posted / asked about, unless there’s some syntactic equivalence that I’m misunderstanding. Specifically, the variants with : inside of {} have docs, and the ones without don’t:

Code:
${parameter:=word} # has doc
${parameter=word} # no doc
${parameter:-word} # has doc
${parameter-word} # no doc
 
Please have a look at the paragraph from sh(1) quoted above which says:
Code:
     ... use of the colon in the
     format results in a test for a parameter that is unset or null; omission
     of the colon results in a test for a parameter that is only unset
 
That makes two things that I don't see documented in sh(1):
Code:
: ${FOO=foo} # bare = inside {}
FOO=${FOO-foo} # bare - inside {}
[...]
For a more elaborate explanation, see Using curly braces with variables where the reasons for the use of ${ } are described, including ${variable=value} - Redefine if undefined and ${variable-default} - Use default if undefined

Parameter expansion ${ } is part of the POSIX standard definition of sh(1): 2.6.2 Parameter Expansion. The table there gives a nice consize overview of its semantics.

The colon command is defined in the POSIX standard at colon - null utility. You'll find:
DESCRIPTION

This utility shall only expand command arguments. It is used when a command is needed, as in the then condition of an if command, but nothing is to be done by the command.
The usage of the colon command in the then condition of an if command is also explained at the end of Commands that must be first on the line

The necessity of using the colon command in : ${FOO=foo} is because of the mechanics (semantics) of parameter expansion, ${FOO=foo} will "evaluate in two steps to":
  1. either the string "foo" (when $FOO was previously undefined)
    or (when $FOO already was defined) to the value it already had (say for example "old")
  2. the string value of $FOO is then interpreted by the sh interpreter as a command to be executed
Example:
Code:
# echo $FOO

# ${FOO=ls}
file1 file2
To prevent that the string value of $FOO gets executed as a command, the colon command is used:
Code:
# : ${FOO=foo}
# echo $?
0
As echo $? shows, its execution always results in a true value (i.e. 0).

As a final note: bash(1) semantics are somewhat different because by default bash is not really POSIX conformant. For some of the colon command semantics, you need to run bash in POSIX mode (e.g. set -o posix). For the bash point of view and the difference with the true command, see What is the Bash Null Command?
 
Back
Top