4

I have the following bash function:

lscf() { while getopts f:d: opt ; do case $opt in f) file="$OPTARG" ;; d) days="$OPTARG" ;; esac done echo file is $file echo days is $days } 

Running this with arguments does not output any values. Only after running the function without arguments, and then again with arguments does it output the correct values:

-bash-4.1$ lscf -d 10 -f file.txt file is days is -bash-4.1$ lscf file is days is -bash-4.1$ lscf -d 10 -f file.txt file is file.txt days is 10 

Am I missing something?

2
  • 2
    local OPTIND at the first of your function
    – cuonglm
    CommentedApr 9, 2018 at 4:35
  • thanks @cuonglm, that solved it. Would you mind explaining why this worked?
    – Subbeh
    CommentedApr 9, 2018 at 4:37

1 Answer 1

5

Though I can't reproduce the initial run of the function that you have in your question, you should reset OPTIND to 1 in your function to be able to process the function's command line in repeated invocations of it.

From the bash manual:

OPTIND is initialized to 1 each time the shell or a shell script is invoked. When an option requires an argument, getopts places that argument into the variable OPTARG. The shell does not reset OPTIND automatically; it must be manually reset between multiple calls to getopts within the same shell invocation if a new set of parameters is to be used.

From the POSIX standard:

If the application sets OPTIND to the value 1, a new set of parameters can be used: either the current positional parameters or new arg values. Any other attempt to invoke getopts multiple times in a single shell execution environment with parameters (positional parameters or arg operands) that are not the same in all invocations, or with an OPTIND value modified to be a value other than 1, produces unspecified results.

The "shell invocation" that the bash manual mentions is the same as the "single execution environment" that the POSIX text mentions, and both refer to your shell script or interactive shell. Within the script or interactive shell, multiple calls to your lscf will invoke getopts in the same environment, and OPTIND will need to be reset to 1 before each such invocation.

Therefore:

lscf() { OPTIND=1 while getopts f:d: opt ; do case $opt in f) file="$OPTARG" ;; d) days="$OPTARG" ;; esac done echo file is $file echo days is $days } 

If the variables file and days should not be set in the calling shell's environment, they should be local variables. Also, quote variable expansions and use printf to output variable data:

lscf() { local file local days OPTIND=1 while getopts f:d: opt ; do case $opt in f) file="$OPTARG" ;; d) days="$OPTARG" ;; esac done printf 'file is %s\n' "$file" printf 'days is %s\n' "$days" } 

    You must log in to answer this question.

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.