6

I'm trying to create a function and believe I found a good working example but I don't understand all the logic behind it.

More specifically, on the "while" line, could someone explain what test is and does? what is $# (isn't # a comment char?) and were does the -gt 0 parameter comes from? couldn't find it in the while man page.

Here is the example:

function my_function()
{
while test $# -gt 0
do
    $
echo "$1"
    shift
done
}

Thank you.

  • Thank you everyone for the answers. They were all very useful. – TCZ8 May 7 '14 at 13:04
10

While # on it's own is definitely a comment, $# contains the number of parameters passed to your function.

test is an program which lets you perform various tests, for example, if one number is greater than another number (if your operator is -gt; there are many other operators, see man test). It will return success if the the test is successful (in this case, if the number of parameters IS greater than 0).

the shift command throws away the first parameter. It also decreases $#

The code as a whole can be seen as: do something with a parameter (in this case, showing it on the screen), then discard it; repeat until there are no parameters left.

If you want to see all the parameters that are left, useful for debugging, check the contents of $@

3

$# ==> script parameters passed

test ==> condition evaluation command

-gt ==> represents greater than

test a -gt b ==> true if a is greater than b, false otherwise

Putting it all together:

while test $# -gt 0 ==> while there are more parameters passed (the reason why this is changing is because of shift)

What make things tricky is shift within the body of the while loop.

$1 ==> represents always the first parameter

To make this more concrete, let's say you are passing parameters a, b and c.

$1 ==> represents a as this is your first parameter

by calling now shift, a is gone and your parameter list is now b and c therefore if you invoke $1 now it's b since this is now the first parameter in the list. Invoking shift once more your parameter list is now just c, therefore $1 is now c. Calling shift one more time leaves the parameter list empty therefore the while condition will not success (as the parameter list if not greater than 0 anymore as it's now zero in size) which will cause the while loop to terminate.

What are the gains of using shift and referring to the current parameter as $1?

It gives you the flexibility not to know in advance in your script how many parameters are passed in the script and regardless of that to iterate through them one by one referring within the while loop to the current param always as $1 since that means the head of the parameter list. By shifting eventually the parameter list will be empty therefore it's important to have the while condition to check whether it's greater than zero in order to terminate and not infinitely loop.

1

Run the below script to understand what the variables mean. Save the script as somescript.sh and call the script with some input parameters.

#!/bin/bash
echo "I display the total parameters passed to this script from commandline"
echo $#
echo "I display all the parameter values"
echo "Input: $@"
echo "I display the first parameter value"
echo "$1"

shift 
echo "After shift: $@"
0

If you'll forgive me a bit of answer recycling, I think you may find this useful.

_fn() { set -- "$@" $(cat)
    while ${1+:} false ; do 
        echo "$1" && [ "$1" = "arg2" ] && echo "$1"
        $YOUR_CHK
        shift
    done
}

echo "arg2" | _fn "arg1"

OUTPUT

arg1
arg2
arg2

That handles both cmd-line args and stdin. It only runs the while loop to check them while you still have at least one argument saved in your parameter array. It does discard every argument it checks, so part of $YOUR_CHK should be saving information you think valuable in one way or another.

_fn would behave the same if its contents were the body of a script, or as is in the form of a shell function.

_fn handles stdin - in this case "arg2" echoed over the |pipe - in the first line by setting its positional parameter shell $@array to everything passed on the command-line - or "$@" - AND everything cat spits out via $(comand substitution).

while _fn's $1 first parameter is ${set+} we substitute the shell's built-in :true to satisfy the while loop conditional. As soon as $1 is unset that substitution fails to happen, the conditional evaluates to false and the while loop breaks.

For every iteration of the while loop the _fn() echoes its $1 first parameter && if successful (echo is always successful) [ tests ] to see if $1 equals the string "arg2" && if the [ test ] is successful _fn() echoes $1 again.

$YOUR_CHK is a null-op - it is an unset variable that evaluates to nothing before ever the shell executes any code.

For every iteration of the while loop we shift away the $1 first parameter. So on iteration 1 "$@" looks like:

arg1 arg2

But after we shift the first time it looks like:

arg2

And after we shift the last time it looks like:

So this brings us back around again to ${1+:} false - because $1 is now unset the shell will not substitute :true and instead only false is evaluated, which breaks the while loop and ends the _fn().

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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