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.

devios1
  • 789

2 Answers2

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 "$@"
  • 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. – Stéphane Chazelas Jul 17 '13 at 07:02
  • this is probably right answer. – Sungguk Lim Apr 09 '14 at 05: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).

Paulo Almeida
  • 746
  • 3
  • 4