【问题标题】:/bin/sh: parsing command line arguments results in: shift: can't shift that many/bin/sh: 解析命令行参数导致: shift: can't shift that many
【发布时间】:2021-11-03 05:09:45
【问题描述】:

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

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

./myscript -P "pattern" file.txt

命令行参数可以是任意顺序,./myscript file.txt -P "pattern" 是允许的。但是-P后面一定要跟pattern。

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

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

# 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 "$1" ] ; then
            pattern="$1"
            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

【问题讨论】:

  • arg 的值是多少?你似乎没有在任何地方设置它
  • 我猜这是因为您已经用完了要转移的 args。
  • @joshmeranda - 这是处理命令行参数的标准方法。默认情况下,如果没有指定列表,for arg do 会通过$@
  • 哦,整洁以前没见过。这是docs 的摘录,如果有人想要的话:“如果'in words' 不存在,for 命令会为每个设置的位置参数执行一次命令,就好像'in "$@"' 已被指定"。

标签: shell sh command-line-arguments getopts


【解决方案1】:

for 循环保留它自己的位置参数列表的私有副本,您无法使用shiftset 更改它(请参阅Modifying positional parameters while iterating over them in POSIX sh)。

改用while循环。

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

p= f=
parse_args "$@"

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-16
    • 1970-01-01
    • 1970-01-01
    • 2013-03-21
    相关资源
    最近更新 更多