问题描述
如果我未能显式调用 那是因为 git 处理别名的方式:
赋予别名
[别名]
myalias =!字符串
其中 string 代码,当调用 git myalias args 其中 args 是一个可能为空的参数列表 git 将执行:
sh - c' string $ @'' string ' args
例如:
[别名]
banana =!echo$ 1,$ 2,SNIP
并呼叫
git banana one'two two'three
git 将执行:
sh -c' echo$ 1,$ 2,SNIP$ @''echo$ 1,$ 2,SNIP'one'two two'three
,所以输出将是:
one,two two,SNIP one two two three
在您的情况下,
[别名]
encrypt-for =!g(){echo \once\;}; $ 1;
并呼叫
git encrypt-for g
git
$ $ $ $ $ $ $ $ $ $ $ $ $ $; '''g(){echoonce;}; $ 1;'g
让我用等价的形式重写它:
sh -c'g(){echoonce;}; $ 1; $ @' - g
我只替换了'g() {echoonce;}; $ 1;'部分(即 sh 的 $ 0 的位置参数,并且不会在这里扮演任何角色)通过一个伪参数 - 。应该清楚,它就像执行:
g(){echoonce;}; g; g
所以你会看到:
一次
一次
补救措施: t使用参数!只需使用:
[别名]
encrypt-for =!g( ){echoonce;};
现在,如果您确实想要使用参数,请确保在给定的尾随参数不在所有。一种可能性是添加一个尾随注释字符,如下所示:
[别名]
encrypt-for =!g (){echoonce;}; $ 1#
也可以将所有内容包装在一个函数中:
$ $ p $ $ $ $ $ {\
case $ 1 in \
(github)echo github ;; \
(twitter)echo twitter ;; \
(facebook)echo facebook ;; \\ \\ $;
(*)echo>& 2 \错误,未知$ 1\; exit 1 ;; \
esac \
}; main
希望您明白 git 正在用别名做的事情!它真的将$ @附加到别名字符串,并用此字符串和给定参数调用 sh -c 。
If I fail to explicitly call exit for certain function-based Bash scripts then there are additional unexpected executions for some functions. What is causing this? The behavior was first noticed while making a git alias as part of answering another user's question on StackOverflow. That alias was composed of this script (which runs the function twice instead of once):
#!/usr/bin/env bash github(){ echo github; }; twitter(){ echo twitter; }; facebook(){ echo facebook; }; if [[ $(type -t "$1") == "function" ]]; then "$1"; else echo "There is no defined function for $1"; fi;
But this slightly modified script executes as expected (runs the function only once):
#!/usr/bin/env bash github(){ echo github; }; twitter(){ echo twitter; }; facebook(){ echo facebook; }; if [[ $(type -t "$1") == "function" ]]; then "$1"; exit 0; else echo "There is no defined function for $1"; exit 1; fi;
Here is exactly what is happening when I run those scripts via a git alias (added set command for debugging purposes only):
$ git config --global alias.encrypt-for '!set -evu -o pipefail;github(){ echo github;};twitter(){ echo twitter;};facebook(){ echo facebook;};if [[ $(type -t "$1") == "function" ]];then "$1"; exit 0; else echo "There is no defined function for $1"; exit 1; fi;' $ git encrypt-for "github" type -t "$1" github $ git config --global alias.encrypt-for '!set -evu -o pipefail;github(){ echo github;};twitter(){ echo twitter;};facebook(){ echo facebook;};if [[ $(type -t "$1") == "function" ]];then "$1"; else echo "There is no defined function for $1"; fi;' $ git encrypt-for "github" type -t "$1" github github
The output from set -x:
$ git encrypt-for "github" ++ type -t github + [[ function == \f\u\n\c\t\i\o\n ]] + github + echo github github + github + echo github github
The output from replacing echo github with echo "I am echo in github" as a way of ruling out the echo command as the source of the second function execution:
$ git encrypt-for "github" ++ type -t github + [[ function == \f\u\n\c\t\i\o\n ]] + github + echo 'I am echo in github' I am echo in github + github + echo 'I am echo in github' I am echo in github
The following is the simplest version of the alias/script which gives the undesired behavior of double execution:
g(){ echo "once"; }; $1;
And this is the resulting output from executing the simplified alias/script (which has the incorrect behavior of executing twice):
$ git config --global alias.encrypt-for '!g(){ echo "once";};$1;' $ git encrypt-for g once once
That's because of the way git handles aliases:
Given an alias
[alias] myalias = !string
where string is any string that represents some code, when calling git myalias args where args is a (possibly empty) list of arguments, git will execute:
sh -c 'string "$@"' 'string' args
For example:
[alias] banana = !echo "$1,$2,SNIP "
and calling
git banana one 'two two' three
git will execute:
sh -c 'echo "$1,$2,SNIP " "$@"' 'echo "$1,$2,SNIP "' one 'two two' three
and so the output will be:
one,two two,SNIP one two two three
In your case,
[alias] encrypt-for = "!g(){ echo \"once\";};$1;"
and calling
git encrypt-for g
git will execute:
sh -c 'g(){ echo "once";};$1;"$@"' 'g(){ echo "once";};$1;' g
For clarity, let me rewrite this in an equivalent form:
sh -c 'g(){ echo "once";};$1;"$@"' - g
I only replaced the 'g(){ echo "once";};$1;' part (that will be sh's $0's positional parameter and will not play any role here) by a dummy argument -. It should be clear that it's like executing:
g(){ echo "once";};g;g
so you'll see:
once once
To remedy this: don't use parameters! just use:
[alias] encrypt-for = "!g(){ echo "once";};"
Now, if you really want to use parameters, make sure that the trailing parameters given are not executed at all. One possibility is to add a trailing comment character like so:
[alias] encrypt-for = "!g(){ echo "once";};$1 #"
For your full example, a cleaner way could also be to wrap everything in a function:
[alias] encrypt-for = "!main() {\ case $1 in \ (github) echo github;; \ (twitter) echo twitter;; \ (facebook) echo facebook;; \ (*) echo >&2 \"error, unknown $1"\; exit 1;; \ esac \ }; main"
Hopefully you understood what git is doing under the hood with aliases! it really appends "$@" to the alias string and calls sh -c with this string and the given arguments.
这篇关于为什么这个Bash函数在一个git别名内执行两次,为什么添加`exit`修复它?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!