/bin/sh:解析命令行参数导致:shift:不能移动那么多

/bin/sh: parsing command line arguments results in: shift: can't shift that many

我需要自己解析命令行参数,而不是依赖 getopt 或其他函数。

我的脚本(下面进行了简化)采用可选参数 -P,后跟模式,然后是文件名:

./myscript -P "pattern" file.txt

命令行参数可以任意顺序,./myscript file.txt -P "pattern"是允许的。但是 -P 后面必须始终跟有模式。

我面临的问题是,当提供 -P 时,我需要“吃掉”下一个参数,这样它就不会被解释为文件名。所以我使用 shift。但似乎 for 循环已经读入了原始的 $@ 数组,所以即使我移动,它仍然迭代“模式”,并将其解释为文件。

所以我有 3 次而不是 2 次 for 循环迭代:

# 1. cycle:
arg = -P
$@ = -P pattern file
pattern =
file =

# 2. cycle:
arg = pattern
$@ = file
pattern = pattern
file =

3. cycle    
arg = file
$@ =
pattern = pattern
file = pattern

我的预期是,for 循环应该是 运行 两次:-Pfile。相反,它是 运行 3 次,我得到这个错误:

shift: can't shift that many

我该如何解决这个问题?

#!/bin/sh

f=
pattern=

 for arg do

    echo arg = $arg
    echo $\@ = $@
    echo pattern = $pattern
    echo file = $f
    echo

    shift

    case "$arg" in

    (-P)
        if [ -n "" ] ; then
            pattern=""
            shift
        else
            printf "\nError: -P needs an argument\n\n" >&2
            exit 1
        fi
        ;;

    (*)
        if [ -z "$f" ] ; then
            f="$arg"
        else
            printf "\nError: too many arguments: $f $arg\n\n" >&2
            exit 1
        fi
        ;;

    esac
 done

for 循环保留其自己的位置参数列表的私有副本,您无法使用 shiftset(请参阅 )更改该副本。

改为使用 while 循环。

parse_args()
  while test $# -gt 0; do
    case  in
    (-P)
      p=
      shift ;;
    (*)
      f=
    esac
    shift
  done

p= f=
parse_args "$@"