我正在创建一个gc
可以区分 git commit/checkout 的智能别名。
如果在不带任何参数或使用,参数gc
的情况下调用,则运行 git commit。否则,运行 git checkout (如果有带有附加参数的标志)。如果调用 gc 的任何其他变体,我希望它抛出错误而不是做一些意想不到的事情。-a
-m
b
这是到目前为止我的 shell 函数。
gc() {
args=$@
commit=false
checkout=false
# Check flags
while getopts ":am:b" opt; do
case $opt in
a|m)
commit=true
;;
b)
checkout=true
;;
\?)
echo "Unknown flags passed."
return 1
;;
esac
done
shift "$((OPTIND-1))"
# Check number of arguments
if [[ "$#" == 0 ]]; then
commit=true
elif [[ "$#" == 1 ]]; then
checkout=true
else
echo "Too many arguments"
return 1
fi
# Finally run respective command
if [[ $commit == true && $checkout == true ]]; then
echo "Unable to ascertain which operation you would like to perform."
return 1
elif [[ $commit == true ]]; then
git commit "$args"
elif [[ $checkout == true ]]; then
git checkout "$args"
else
echo "Undefined behavior"
return 1
fi
}
但是,这不能正常工作。经过一番实验后,我发现分配$@
给另一个变量是根本原因,但我无法理解为什么以及到底出了什么问题。
另外,由于我是第一次编写 shell 函数,请突出显示我所犯的任何错误。
答案1
$@
是一个数组,将其赋值给一个数组:
args=("$@")
然后将其用作数组:
elif [[ $commit == true ]]; then
git commit "${args[@]}"
elif [[ $checkout == true ]]; then
git checkout "${args[@]}"
else
当前代码中发生的情况是,所有单独的参数都存储为单个字符串。所以如果你打电话:
bc -a "foo bar"
被分配args
为:
args='-a foo bar'
然后而不是执行:
git commit -a "foo bar"
你得到:
git commit '-a foo bar'
其他一些注意事项:
- 在 zsh 中,您还可以使用
"$args[@]"
或"${(@)args}"
代替"${args[@]}"
,但在 bash/ksh 中则不然。"$args"
在 yash 中可以工作,但在 中,这与(与 的第一个字符连接的元素)zsh
相同,在 ksh/bash 中与 相同。在 zsh 中,可以工作,但会删除空元素(如果有)。"$args[*]"
$IFS
"${args[0]}"
$args
getopts
在函数中使用时,您应该事先添加一些local OPTIND OPTARG
(尤其是OPTIND
或至少初始化/取消设置它)。 zshOPTIND
在不模拟其他 shell 时会自动执行此操作,但 ksh/bash/yash 不会。您在函数中使用的其他变量也应该设置为本地变量。- 错误应该发送到 stderr (fd 2):
echo>&2 Too many arguments
等等。
答案2
由于另一个答案不是 POSIX,因此这里有一个替代方案。如果你想暂时粉碎位置参数,你可以这样做:
s1=$(printf '%s\n' "$@")
然后当您准备好恢复时,请执行以下操作:
IFS='
'
set -- $s1
请注意,这假设参数不包含换行符。如果这样做,则需要使用不同的分隔符。
旁注:如果您注意到,最后一行包含一个未加引号的变量。这是可以接受不带引号的变量的少数情况之一(在我看来,是唯一的情况):用户已明确设置IFS
.用户本质上是说“是的,我知道我在做什么,请继续”。