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.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I am currently following a tutorial about shell scripting located here: http://www.cs.columbia.edu/~sauce/tutorial/ashell.html and I believe my script matches that one exactly. However, when I try to run the example, my output does not match. Since I don't want to be vague and simply ask "why doesn't it work?", I will focus on the part I don't understand: why is there a ":" after the testlogin: command? I have read many forums that discuss the meaning of " : " (with spaces on either side) and also a leading ":", but none mention the use of the lagging colon. What is the meaning in this context? And if this is a typo, can anyone help me find another typo in the example that might be making it not run correctly?

Any help would be greatly appreciated!

the code (copied exactly from the site I linked above) is below:

#testlogin
useron(){
if ( who | grep $1 > /dev/null)
then echo $1 is logged in
else echo $1 is not logged in
fi 
}
if test $# != 1
then echo testlogin: username
else useron
fi

and the output of

paul@paul-LC22UP:~$ .testlogin paul
Usage: grep [OPTION]... PATTERN [FILE]...
Try 'grep --help' for more information.
is no logged in
share|improve this question

closed as off-topic by Michael Homer, cuonglm, Jakuje, Stephen Kitt, nwildner Jan 18 at 9:49

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Questions describing a problem that can't be reproduced and seemingly went away on its own (or went away when a typo was fixed) are off-topic as they are unlikely to help future readers." – Michael Homer, cuonglm, Jakuje, Stephen Kitt, nwildner
If this question can be reworded to fit the rules in the help center, please edit the question.

    
In that particular context, I don't think it has any meaning, it's just part of the literal string testlogin: username that's echoed as a usage message if the script is called without the expected username argument. However I'd question the provenance of that webpage - it doesn't even indicate which shell the script is intended for: if it's meant to be for bash, then I'd expect the argument to be passed explicitly to the useron function i.e. else useron "$1" rather than just else useron – steeldriver Jan 18 at 0:53
    
@steeldriver, thank you for the tip -- the bug must be somewhere else in the code, and based on your point, I am guessing that it has to do with the particular shell I am using. I would note that the script starts with the generic #testlogin rather than #!/bin/bash -- so maybe it is intended to be completely generic? I will try running it in the other shells I have to see if it works. Otherwise I suppose I will move on. And to think I had finally found a reliable source with which to build my first working shell script... – Paul Jan 18 at 0:59
2  
Please see the nice comprehensive answer by @lvc - #testlogin is just a comment here (probably intended to indicate the filename) – steeldriver Jan 18 at 1:02
    
aha, makes sense. and your intuition about excplicitly passing the $1 argument was correct. I guess it was intended to be a generic script afterall, and the problem was a simple typo. still a great page in my opinion. – Paul Jan 18 at 1:05
up vote 8 down vote accepted

testlogin isn't a command here, but an argument to echo. The full command is:

 echo testlogin: username

echo just spits out the text you give it to STDOUT. So the colon has no special meaning, it is part of the verbatim output of echo. This is the output if you call the script with the wrong number of arguments:

$ ./testlogin
testlogin: username
$ ./testlogin fred bill adam
testlogin: username

This is a strange output, but not necessarily a typo. I would usually expect something more along the lines of:

$ ./testlogin
Usage: testlogin username

The reason the whole script doesn't work is because the author has made a subtle mistake in calling the useron function, and presumably hasn't tested their script before posting it (or they would have noticed the same error you did). $1 in the useron function is the first argument to that function, not to the whole script; but since useron wasn't called with any arguments, that is the empty string and so it ends up running the command:

$ who | grep

which will give greps usage info, and then always trigger the else branch of the condition. You can fix this by passing the first argument to the script into useron like this:

else useron $1
share|improve this answer
    
great! This not only solves my problem but also clears up some general confusion about the scripting environment. – Paul Jan 18 at 1:02

The site you link to has very low quality shell scripts.
To answer your question up-front, the "trailing colon" has no special meaning to the shell, it is just part of an string to be printed on the screen.

However the code on the page has this flaws:

  • The most important issue that you should learn is "quote your expansions".
    That is "Parameter Expansions" (variable), but also other expansions.
    This is the main reason I am writing this. It is very bad to get into the habit of failing to quote expansions.

  • There is no mention of the shebang mechanism anywhere in the page. (it means to have a first line like #!/bin/bash or similar). Writing scripts without stating the type of shell meant to run them is a sure source of bugs and problems.

  • Line number 11 is missing the argument that is used to call the function. It should be something like: else useron "$1" instead of else useron

After those three clear mistakes, we can run the script:

$ testlogin
testlogin: username

As you can see, the string on line 10 is printed as is on the console.
The trailing colon : does not mean anything to the shell, which becomes clear when the string is quoted as this:

then echo "testlogin: username"

In fact, IMO, It would be be even more explicit:

then echo "A parameter is needed, type something like 'testlogin: username'"

Now the execution of the script without an argument prints a helpful answer.
And writing an username also work:

$ testlogin joe
joe is not logged in

The script with the changes above is:

#/bin/sh
useron(){
    if    ( who | grep "$1" > /dev/null)
    then  echo "$1 is logged in"
    else  echo "$1 is not logged in"
    fi
}

if    test "$#" != "1"
then  echo "Parameter missing, type something like 'testlogin: username'"
else  useron "$1"
fi

Now, for a second round of issues:

  • The test external command has fallen in disuse on most shells.
    It has been replaced by the fully equivalent [ or by [[ in more modern shells. The line used in the script: if test "$#" != "1" should be replaced by:

    if    [ "$#" != "1" ]
    
  • Now we get to writing style. On modern times, it is more common to see the script written like this (but that is a matter of personal preference):

#/bin/sh
useron(){
    if ( who | grep "$1" > /dev/null); then
        echo "$1 is logged in"
    else
        echo "$1 is not logged in"
    fi
}

if  [ "$#" != "1" ]; then
    echo "Parameter missing, type something like 'testlogin: username'"
else
    useron "$1"
fi
share|improve this answer
    
thank you for your suggestions. I will remember the parameter suggestion pattern in the future, and will make sure to also quote my expansions. It seems simpler that way anyways. – Paul Jan 18 at 20:45

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