1

This seems like a weird question to have, but I need to write a simple bash script that takes two arguments from the command line. The script looks like this:

#!/bin/bash

while getopts a:b: flag; do
    case "${flag}" in 
        a) optionA="${OPTARG}";;
        b) optionB="${OPTARG}";;
    esac
done

echo $optionA
echo $optionB

When I run . script.sh -a optionA -b optionB it works exactly as expected and echos the two inputs. The issue is if I run the exact same script again, the arguments are not echoed and I just see two blank lines. Only the first time running it produces the correct behavior. If I close the terminal session and try again it works, but I'd like to be able to run it several times in a row.

I was thinking the issue had something to do with the variables remaining and then being overwritten by the next run so I tried something like unset optionA, but it did not help.

I apologize if this is a simple question, but I could not seem to find an answer anywhere else and I would much appreciate any help. Thanks!

EDIT: The simple solution is that I was sourcing the script when I could have been running it. Using bash test.sh -a optionA -b optionB works just fine.

EDIT 2: If there is a need to source instead of run, the answer Charles gave also works; use unset OPTIND after each run.

  • 2
    _Sourcing_ a script and _running_ the script are quite different things. – Charles Duffy Jul 07 '21 at 15:22
  • 3
    ...because you're sourcing the script, `OPTARG` and `OPTIDX` are left in place between runs. – Charles Duffy Jul 07 '21 at 15:22
  • 1
    That said, I can't reproduce this -- works fine. As it should: `optionA` is left in place after your first run, so even if the `while getopts` doesn't do anything on your second run, `echo $optionA` still prints the option parsed during the initial invocation. If you have a _real_ reproducer that causes the problem, please make sure the [mre] in the question still does likewise. – Charles Duffy Jul 07 '21 at 15:25
  • Oh okay that makes sense! I knew I had to be doing something dumb. Thanks for the help! – theoneandonlyC2 Jul 07 '21 at 15:26
  • See the code at http://ix.io/3sem re: how I tested this for the "works fine" assertion -- try plugging that into https://replit.com/languages/bash – Charles Duffy Jul 07 '21 at 15:28
  • (pardon above, wrote `OPTIDX` before checking the actual name; it should be `OPTIND`). – Charles Duffy Jul 07 '21 at 15:30
  • Yeah the example I included in the question does the same thing where it doesn't echo after the first run. Not sure why that would be, but for my purposes running the script is sufficient anyway. – theoneandonlyC2 Jul 07 '21 at 15:32
  • BTW, if you're not happy with the answers added and want to add your own, use the "Add an Answer" button to do that instead of editing an answer into the question itself. – Charles Duffy Jul 07 '21 at 15:32
  • That shebang line looks off, it should be `#!/bin/bash`, not `#~/bin/bash` (or, [arguably](https://stackoverflow.com/q/10376206/3266847), `#!/usr/bin/env bash`) – Benjamin W. Jul 07 '21 at 15:32
  • (See also [What to do when OP answers his/her own question in an edit?](https://meta.stackexchange.com/questions/74101/what-to-do-when-op-answers-his-her-own-question-in-an-edit) over on [meta]) – Charles Duffy Jul 07 '21 at 15:34

1 Answers1

0

To reset getopts between sourced invocations of your script, unset OPTIND.

This variable tells getopts how many items have already been processed, and thus how many should be skipped.


By contrast, if you just run your script without sourcing it, this variable won't be held over, so there's nothing to reset.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441