bash - 如果需要操作数,getopts 只解析第一个参数
bash - getopts only parses the first argument if operands are required
一旦 bash 程序在处理 getops
中的选项时执行,循环就会退出。
作为一个简短的例子,我有以下 bash 脚本:
#!/usr/bin/env bash
while getopts ":a:l:" opt; do
case ${opt} in
a)
ls -a
;;
l)
ls -l
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument" >&2
exit 1
;;
esac
done
echo -e "\nTerminated"
如果脚本被调用 test.sh
,当我用这个命令执行脚本时,我得到以下输出,其中只处理了 -a
标志,而 -l
是忽略:
$ ./test.sh -al .
. .. file1.txt file2.txt test.sh
Terminated
但是,如果我删除了每个参数后的冒号,表明每个参数都不需要操作数,那么脚本会按预期运行。如果while
循环改为:
while getopts ":al" opt; do
然后,运行 我的脚本给出了以下输出(同时处理了 -a
和 -l
):
$ ./test.sh -al .
. .. file1.txt file2.txt test.sh
total 161
-rwxrwxrwx 1 root root 0 Nov 24 22:31 file1.txt
-rwxrwxrwx 1 root root 0 Nov 24 22:32 file2.txt
-rwxrwxrwx 1 root root 318 Nov 24 22:36 test.sh
Terminated
此外,在我的循环末尾添加类似 OPTIND=1
的内容只会导致执行第一个参数的脚本无限循环。
如何让 getopts
解析带有选项参数的多个参数(:
在每个参数之后)?
仅就短选项而言,选项与其参数之间不需要 space,因此 -o something
等于 -osomething
。尽管将它们分开很常见,但也有一些例外,例如:cut -d: -f1
。
就像@AlexP 说的,如果您使用while getopts ":a:l:" opt
,那么选项-a
和-l
应该有一个参数。当您将 -al
传递给您的脚本并使选项 -a
需要一个参数时,getopts
查找它并基本上看到这个:-a l
这就是它忽略的原因-l
选项,因为 -a
"ate it".
您的代码有点乱,正如@cdarke 所建议的,它没有使用 getopts
提供的方法,例如 $OPTARG
。您可能需要查看此 getopts tutorial。
如果我没理解错的话,您的主要目标是检查 file/folder 是否已传递给 ls
的脚本。您不是通过使选项需要参数来实现这一点,而是通过检查是否有 file/folder after 所有选项来实现。你可以用这个来做到这一点:
#!/usr/bin/env bash
while getopts ":al" opt; do
case ${opt} in
a) a=1 ;;
l) l=1 ;;
\?) echo "Invalid option: -$OPTARG" >&2; exit 1 ;;
:) echo "Option -$OPTARG requires an argument" >&2; exit 1 ;;
esac
done
shift $(( OPTIND - 1 ));
[[ "$#" == 0 ]] && { echo "No input" >&2; exit 2; }
input=("$@")
[[ "$a" == 1 ]] && ls -a "${input[@]}"
[[ "$l" == 1 ]] && ls -l "${input[@]}"
echo Done
此解决方案将您由选项触发的选择保存到变量(您可以使用数组代替),然后根据这些变量做出决定。保存到 variables/array 可以为您提供更大的灵活性,因为您可以在脚本中的任何位置使用它们。
处理完所有选项后,shift $(( OPTIND - 1 ));
丢弃所有选项和相关参数,只留下不属于任何选项的参数 = 您的 files/folders。如果没有任何 files/folders,您可以使用 [[ "$#" == 0 ]]
检测并退出。如果有,您将它们保存到一个数组 input=("$@")
并在稍后决定您的变量时使用此数组:
[[ "$a" == 1 ]] && ls -a "${input[@]}"
[[ "$l" == 1 ]] && ls -l "${input[@]}"
此外,与 ls -a
不同,使用数组 ls -a "${input[@]}"
让您可以传递多个 file/folder:./test.sh -la . "$HOME"
.
一旦 bash 程序在处理 getops
中的选项时执行,循环就会退出。
作为一个简短的例子,我有以下 bash 脚本:
#!/usr/bin/env bash
while getopts ":a:l:" opt; do
case ${opt} in
a)
ls -a
;;
l)
ls -l
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument" >&2
exit 1
;;
esac
done
echo -e "\nTerminated"
如果脚本被调用 test.sh
,当我用这个命令执行脚本时,我得到以下输出,其中只处理了 -a
标志,而 -l
是忽略:
$ ./test.sh -al .
. .. file1.txt file2.txt test.sh
Terminated
但是,如果我删除了每个参数后的冒号,表明每个参数都不需要操作数,那么脚本会按预期运行。如果while
循环改为:
while getopts ":al" opt; do
然后,运行 我的脚本给出了以下输出(同时处理了 -a
和 -l
):
$ ./test.sh -al .
. .. file1.txt file2.txt test.sh
total 161
-rwxrwxrwx 1 root root 0 Nov 24 22:31 file1.txt
-rwxrwxrwx 1 root root 0 Nov 24 22:32 file2.txt
-rwxrwxrwx 1 root root 318 Nov 24 22:36 test.sh
Terminated
此外,在我的循环末尾添加类似 OPTIND=1
的内容只会导致执行第一个参数的脚本无限循环。
如何让 getopts
解析带有选项参数的多个参数(:
在每个参数之后)?
仅就短选项而言,选项与其参数之间不需要 space,因此 -o something
等于 -osomething
。尽管将它们分开很常见,但也有一些例外,例如:cut -d: -f1
。
就像@AlexP 说的,如果您使用while getopts ":a:l:" opt
,那么选项-a
和-l
应该有一个参数。当您将 -al
传递给您的脚本并使选项 -a
需要一个参数时,getopts
查找它并基本上看到这个:-a l
这就是它忽略的原因-l
选项,因为 -a
"ate it".
您的代码有点乱,正如@cdarke 所建议的,它没有使用 getopts
提供的方法,例如 $OPTARG
。您可能需要查看此 getopts tutorial。
如果我没理解错的话,您的主要目标是检查 file/folder 是否已传递给 ls
的脚本。您不是通过使选项需要参数来实现这一点,而是通过检查是否有 file/folder after 所有选项来实现。你可以用这个来做到这一点:
#!/usr/bin/env bash
while getopts ":al" opt; do
case ${opt} in
a) a=1 ;;
l) l=1 ;;
\?) echo "Invalid option: -$OPTARG" >&2; exit 1 ;;
:) echo "Option -$OPTARG requires an argument" >&2; exit 1 ;;
esac
done
shift $(( OPTIND - 1 ));
[[ "$#" == 0 ]] && { echo "No input" >&2; exit 2; }
input=("$@")
[[ "$a" == 1 ]] && ls -a "${input[@]}"
[[ "$l" == 1 ]] && ls -l "${input[@]}"
echo Done
此解决方案将您由选项触发的选择保存到变量(您可以使用数组代替),然后根据这些变量做出决定。保存到 variables/array 可以为您提供更大的灵活性,因为您可以在脚本中的任何位置使用它们。
处理完所有选项后,shift $(( OPTIND - 1 ));
丢弃所有选项和相关参数,只留下不属于任何选项的参数 = 您的 files/folders。如果没有任何 files/folders,您可以使用 [[ "$#" == 0 ]]
检测并退出。如果有,您将它们保存到一个数组 input=("$@")
并在稍后决定您的变量时使用此数组:
[[ "$a" == 1 ]] && ls -a "${input[@]}"
[[ "$l" == 1 ]] && ls -l "${input[@]}"
此外,与 ls -a
不同,使用数组 ls -a "${input[@]}"
让您可以传递多个 file/folder:./test.sh -la . "$HOME"
.