6

Bash is driving me nuts. I can't figure out why the following (nor any of the dozens of variations I've literally copied and pasted from examples) fails to work:

#!/bin/bash

echo $#

function main {
    if (( $# < 1 )); then
        usage
    fi

    echo "good"
}

function usage {
    echo "Usage: $0 <outputdir>"
    exit 1
}

main

For some reason the argument check fails every time, even when I pass an argument and can clearly see $# is set as expected.

1

2 Answers 2

11

You're calling the main function with no argument. So $# in the main function is always 0.

Each function instance has its own arguments. (“Instance” means that if the function is started multiple times (through recursive calls), each call has its own arguments.) The positional parameters $1, $2, etc., as well as the associated parameters $#, $* and $@ refer to the arguments passed in the function call. For example, if you call main foo bar then inside the main function the value of $1 will be foo, the value of $# will be 2 and so on.

If you want to pass the arguments of the script to a function, use "$@". This construct expands to the list of arguments passed to the script (or to the function, if called inside a function). Note that unlike what normally happens with double quotes, the parameters are passed separately, "$@" is a list of strings and not a single string. The double quotes are necessary, otherwise the parameters are not passed as is but treated as a whitespace-separated list of file name patterns.

function main {
  if (( $# < 1 )); then
    usage
  fi
  echo "good"
}
main "$@"
2
  • As an extra note, the function foo { syntax is the ksh function definition syntax (as opposed to the Bourne/POSIX f() { one) and in ksh, with that syntax, $0 is also overridden inside the function. $0 is overridden whatever the syntax in zsh (unless in sh emulation) and is never overridden in other shells like ash, yash or bash. Jul 17, 2013 at 7:02
  • this is probably right answer. Apr 9, 2014 at 5:20
7

This should work:

#!/bin/bash
set -x
NARGS=$#

function main {
    if [ $NARGS -lt 1 ]; then
        usage
    fi

    echo good
}

function usage {
    echo "Usage: $0 <outputdir>"
    exit 1
}

main

I left the set -x there on purpose. It's useful to debug these errors. If you don't pass any arguments to the main function, $# becomes 0 inside it (replace $NARGS with $# in the if condition to see this).

4
  • I see! So calling a function clobbers (some of?) the argument variables. Good to know! (Obviously very new to bash, sorry)
    – devios1
    Jul 16, 2013 at 22:17
  • 2
    I didn't know either, thus the usefulness of set -x :) Jul 16, 2013 at 22:24
  • 1
    @chaiguy Only $1, $2, etc get clobbered, as they get re-used for the function arguments. main $NARGS is pointless as the argument isn't being used. $NARGS becomes $1 inside the function. Jul 16, 2013 at 23:12
  • @DavidBaggerman, Thanks. Edited to correct and avoid confusion. Jul 16, 2013 at 23:47

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.