开发者

How to wrap another shell still passing $OPTIND as-is?

开发者 https://www.devze.com 2023-03-01 19:24 出处:网络
I\'m trying to wrap a bash script b with a script a. However I want to pass the options passed to a also to b as they are.

I'm trying to wrap a bash script b with a script a. However I want to pass the options passed to a also to b as they are.

#!/bin/bash

# script a
./b ${@:$OPTIND}

This will also print $1 (if any). What's the simplest way not to?

So calling:

./a -c -d 5 first-arg
开发者_如何学C

I want b to execute: ./b -c -d 5 # WITHOUT first-arg


In bash, you can build an array containing the options, and use that array to call the auxiliary program.

call_b () {
  typeset -i i=0
  typeset -a a; a=()
  while ((++i <= OPTIND)); do   # for i=1..$OPTIND
    a+=("${!i}")                # append parameter $i to $a
  done
  ./b "${a[@]}"
}
call_b "$@"

In any POSIX shell (ash, bash, ksh, zsh under sh or ksh emulation, …), you can build a list with "$1" "$2" … and use eval to set different positional parameters.

call_b () {
  i=1
  while [ $i -le $OPTIND ]; do
    a="$a \"\$$i\""
    i=$(($i+1))
  done
  eval set -- $a
  ./b "$@"
}
call_b "$@"

As often, this is rather easier in zsh.

./b "${(@)@[1,$OPTIND]}"


Why are you using ${@:$OPTIND} and not just $@ or $*?

The ${parameter:index} syntax says to use index to parse $parameter. If you're using $@, it'll use index as an index into the parameters.

$ set one two three four #Sets "$@"
$ echo $@
one two three four
$ echo ${@:0}
one two three four
$ echo ${@:1}
one two three four
$ echo ${@:2}
two three four

$OPTIND is really only used if you're using getopts. This counts the number of times getopts processes the parameters in $@. According to the bash manpage:

OPTIND is initialized to 1 each time the shell or a shell script is invoked.

Which may explain why you're constantly getting the value of 1.


EDITED IN RESPONSE TO EDITED QUESTION

@David - "./b $@ " still prints the arguments of passed to a (see Q edit). I want to pass only the options of a and not the args

So, if I executed:

$ a -a foo -b bar -c fubar barfu barbar

You want to pass to b:

$ b -a foo -b bar -c fubar

but not

$ b -arg1 foo -arg2 bar -arg3 fubar barfu barbar

That's going to be tricky...

Is there a reason why you can't pass the whole line to b and just ignore it?

I believe it might be possible to use regular expressions:

$ echo "-a bar -b foo -c barfoo foobar" | sed 's/\(-[a-z] [^- ][^- ]*\)  *\([^-][^-]*\)$/\1/'
-a bar -b foo -c barfoo

I can't vouch that this regular expression will work in all situations (i.e. what if there are no parameters?). Basically, I'm anchoring it to the end of the line, and then matching for the last parameter and argument and the rest of the line. I do a replace with just the last parameter and argument.

I've tested it in a few situations, but you might simply be better off using getopts to capture the arguments and then passing those to b yourself, or simply have b ignore those extra arguments if possible.


In order to separate the command options from the regular arguments, you need to know which options take arguments, and which stand alone.
In the example command:

./a -c -d 5 first-arg
  • -c and -d might be standalone options and 5 first-arg the regular arguments
  • 5 might be an argument to the -d option (this seems to be what you mean)
  • -d might be an argument to the -c option and (as in the first case) 5 first-arg the regular arguments.

Here's how I'd handle it, assuming -a, -b, -c and -d are the only options, and that -b and -d is the only ones that take an option argument. Note that it is necessary to parse all of the options in order to figure out where they end.

#!/bin/bash

while getopts ab:cd: OPT; do
    case "$OPT" in
        a|b|c|d) : ;; # Don't do anything, we're just here for the parsing
        ?)  echo "Usage: $0 [-ac] [-b something] [-d something] [args...]" >&2
            exit 1 ;;
    esac
done

./b "${@:1:$((OPTIND-1))}"

The entire while loop is there just to compute OPTIND. The ab:cd: in the getopts command defines what options are allowed and which ones take arguments (indicated by colons). The cryptic final expression means "elements 1 through OPTIND-1 of the argument array, passed as separate words".

0

精彩评论

暂无评论...
验证码 换一张
取 消