Why is $# always 0 in my function?

5,674

Solution 1

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 "$@"

Solution 2

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).

Share:
5,674

Related videos on Youtube

devios1
Author by

devios1

Updated on September 18, 2022

Comments

  • devios1
    devios1 over 1 year

    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.

  • devios1
    devios1 almost 11 years
    I see! So calling a function clobbers (some of?) the argument variables. Good to know! (Obviously very new to bash, sorry)
  • Dophlin
    Dophlin almost 11 years
    I didn't know either, thus the usefulness of set -x :)
  • Franco Ferrari
    Franco Ferrari almost 11 years
    @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.
  • Dophlin
    Dophlin almost 11 years
    @DavidBaggerman, Thanks. Edited to correct and avoid confusion.
  • Stéphane Chazelas
    Stéphane Chazelas almost 11 years
    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.
  • Sungguk Lim
    Sungguk Lim about 10 years
    this is probably right answer.