shell-script command-line arguments – Get First CLI Argument After Options in Shell Script

argumentscommand lineshell-script

I have a script I class like so gitploy up -t 2.0.0 test_repo. I pull out the "action" up right away, then I need to be able to get the test_repo before I process the options. I don't want to lose that arguments in line, if that makes sense.. don't want to shift it away, just get it and let it be? Basically I want to get that test_repo spot before I do

while getopts "zhvdurqw:c:i:e:o:b:t:f:p:g" opt; do
  case $opt in

    #flag----------------------
    h)
      usage;
      exit 0
      ;;
    #callback------------------
    c) queue_callback "$OPTARG"
      shift $((OPTIND-1)); OPTIND=1
      ;;
### so one and so forth

section of the script. So basically I can do something like this

# i would get the first argument after the options here first so "test_repo" 
# would be a $@ or $* or something?
root_arg="test_repo"

while getopts "zhvdurqw:c:i:e:o:b:t:f:p:g" opt; do
  case $opt in

    #flag----------------------
    h)
      usage;
      exit 0
      ;;
    #callback------------------
    c) queue_callback "$OPTARG"
      echo "$root_arg was here"
      shift $((OPTIND-1)); OPTIND=1
      ;;
### so one and so forth

In a broader scope of this maybe the question I would guess is, "How to get an arg by position relative to the options?"


revisal of question:

The hope is to say something like,

  • positon of getopts output in command in var
  • call the position of getopts output +1
    • test_repo argument is located +1 after getopts output
  • do normal processing

I'm trying to not move the command for backward compatibility reasons to start with, but not really limited to that. I figured I could just write a little shim here since I know the pattern is already set to gitploy <__action> [__options] <__alias> [__remote_url].

I guess I could make everything an option and deprecate the other arguments. Not sure that is a bad way to do this but it would seem that I would have to scan in an order to look for the <__alias> (or the test_repo as presented in the example) and look for it as an option such as -a test_repo.

Even if it is not the right way to do this in the end, I would like to have an answer on if you can "read the cursor" here and determine the argument value or if is an impossible thing to do.

result of answer below

while getopts "zhvdurqw:c:i:e:o:b:t:f:p:g" opt; do
    case "$opt" in
    esac
done

index="$((OPTIND))"
GD_REPO="${!index}";
OPTIND=1

This what I ended up doing. Seems like dirty trick to fast-forward then rewind, but it works. If there is better ideas, I would still love learn them.

Best Answer

Your requirements are logically contradictory. Given input like gitploy up -t 2.0.0 test_repo, you need to parse the options, and in particular notice the presence of the -t option and the fact that it takes one argument, in order to identify that the first non-option argument is test_repo.

So first parse the options normally. Then you can know what the first operand is. At that point, process the options. Store the information you need about the options in variables.

action="$1"
shift
unset t_arg
while getopts "zhvdurqw:c:i:e:o:b:t:f:p:g" opt; do
  case "$opt" in
    c) c_arg="$OPTARG";;
    …
  esac
done
shift "$((OPTIND-1))"
root_arg="$1"

if [ -n "${c_arg+1}" ]; then
  queue_callback "$OPTARG"
  echo "$root_arg was here"
fi