我该如何编写一个Bash脚本,使其能接受任意顺序的参数?

4
我想知道编写Bash脚本时应该使用哪些原则,以便以任何给定的顺序获取其参数。 例如: - 假设我的脚本名为:script.sh - 我希望它接受零个或至少两个参数。 - 现在假设一个标准参数是“-f”,它指定了下一个参数是我要处理的文件的名称。 - 再次假设给定的文件名为“input.dat”。 - 最后(为了举例),假设我可以添加的最后两个参数名为“-print”和“-delete”。
我在这里提出的问题是: 是否有一种特定的方式(甚至是编程技术)可以使参数以任何给定的顺序传递(除了文件名必须始终遵循“-f”参数之外)?
以下是一些调用示例: 1. ./script.sh -f input.dat -print 2. ./script.sh -print -f input.dat
上述两个执行应产生完全相同的结果!
请注意,实际问题具有更多的参数和不同的结果!

8
你可能想要阅读有关getopt命令的内容。 - Some programmer dude
2个回答

4
这是我编写的脚本以实现这个目标:
#!/bin/bash

# The code below was written to replace the -print and -delete options for
# -p and -d respectively, because getopts can't handle long args, I suggest
# you only use arguments of single letters to make your code more simple, but if 
# you can't avoid it then this is a workaround

for ARGS in "$@"; do
shift
        case "$ARGS" in
                "-print") set -- "$@" "-p" ;;
                "-delete") set -- "$@" "-d" ;;
                *) set -- "$@" "$ARGS"
        esac
done
# getopts works like this: you put all your arguments between single quotes
# if you put a ':' character after the argument letter (just like I did with
# the 'f' letter in the example below), it means this argument NEEDS an extra
# parameter. If you just use letters without the ':' it means it doesn't need
# anything but the argument itself (it's what I did for the '-p' and '-d' options)

while getopts 'f:pd' flag; do
        case "${flag}" in
                f) FILE=${OPTARG} ;;
                p) COMMAND="print" ;;
                d) COMMAND="delete" ;;
        esac
done

echo "$COMMAND $FILE"

以下是它运行的示例:
$ ./script.sh -f filename -print
print filename
$ ./script.sh -print -f filename
print filename
$ ./script.sh -f filename -delete
delete filename
$ ./script.sh -delete -f filename
delete filename

在这里找到了解决长参数问题的解决方法:https://dev59.com/tWct5IYBdhLWcg3wh91K#30026641 - AFAbyss
只有一个问题:如果我们知道在-f之后的第二个参数是需要读取的参数(比如文件名),并且我们知道它总是作为-f之后的第二个参数出现(也就是说:-f 文件名 额外参数),那么我该如何使用getopts来读取它呢? - Christos Maris
我不确定我理解了你的问题,但我认为你的意思是你想要像上面的例子一样在变量“FILE”中存储2个参数,对吗?如果是这样,你可以简单地使用“-f”filename extraParameter"。将整个参数放在双引号之间。 - AFAbyss

2
前面的解决方案是一个很好的起点,我对它进行了一些调整,使其能够接受多个参数,并且我认为这种风格更好一些(也少了一些未使用的变量):
#!/bin/bash
while [ $# -gt 0 ]; do
    case "$1" in
        --file|-f) filename=$2 ; shift;;
        --print|-p) printCommand ;;
        --delete|-d) deleteCommand ;;
        *) break
    esac
shift
done

此外请注意,shift 命令可以接受移动的步数作为参数,这有助于保持代码整洁。例如:
--two-files) filename1=$2; filename2=$3; shift 2;;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接