为什么这个 Bash 函数在 git 别名中执行两次,为什么添加 `exit` 可以修复它?
Why is this Bash function within a git alias executing twice, and why does adding `exit` fix it?
如果我没有明确调用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 。该别名由以下脚本组成(其中 运行 函数两次而不是一次 ):
#!/usr/bin/env bash
github(){
echo github;
};
twitter(){
echo twitter;
};
facebook(){
echo facebook;
};
if [[ $(type -t "") == "function" ]];
then
"";
else
echo "There is no defined function for ";
fi;
但是这个稍微修改过的脚本按预期执行(运行函数只执行一次):
#!/usr/bin/env bash
github(){
echo github;
};
twitter(){
echo twitter;
};
facebook(){
echo facebook;
};
if [[ $(type -t "") == "function" ]];
then
"";
exit 0;
else
echo "There is no defined function for ";
exit 1;
fi;
这正是我通过 git 别名 运行 这些脚本时发生的事情(添加 set
命令仅用于调试目的 ):
$ git config --global alias.encrypt-for '!set -evu -o pipefail;github(){ echo github;};twitter(){ echo twitter;};facebook(){ echo facebook;};if [[ $(type -t "") == "function" ]];then ""; exit 0; else echo "There is no defined function for "; exit 1; fi;'
$ git encrypt-for "github"
type -t ""
github
$ git config --global alias.encrypt-for '!set -evu -o pipefail;github(){ echo github;};twitter(){ echo twitter;};facebook(){ echo facebook;};if [[ $(type -t "") == "function" ]];then ""; else echo "There is no defined function for "; fi;'
$ git encrypt-for "github"
type -t ""
github
github
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
将 echo github
替换为 echo "I am echo in github"
作为排除 echo
命令作为第二个函数执行源的方式的输出:
$ 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
以下是 alias/script 的最简单版本,它给出了双重执行的不良行为:
g(){
echo "once";
};
;
这是执行简化的 alias/script 的结果输出( 执行两次 的行为不正确):
$ git config --global alias.encrypt-for '!g(){ echo "once";};;'
$ git encrypt-for g
once
once
这是因为 git
处理别名的方式:
给定别名
[alias]
myalias = !string
其中 <em>string</em>
是表示某些代码的任何字符串,当调用 git myalias <em>args</em>
其中 <em>args</em>
是一个(可能为空的)参数列表,git
将执行:
sh -c 'string "$@"' 'string' <em>args</em>
例如:
[alias]
banana = !echo ",,SNIP "
并打电话
git banana one 'two two' three
git
将执行:
sh -c 'echo ",,SNIP " "$@"' 'echo ",,SNIP "' one 'two two' three
所以输出将是:
one,two two,SNIP one two two three
在你的情况下,
[alias]
encrypt-for = "!g(){ echo \"once\";};;"
并打电话
git encrypt-for g
git
将执行:
sh -c 'g(){ echo "once";};;"$@"' 'g(){ echo "once";};;' g
为清楚起见,让我以等效形式重写:
sh -c 'g(){ echo "once";};;"$@"' - g
我只将 'g(){ echo "once";};;'
部分(将是 sh
的 [=29=]
的位置参数,在这里不会发挥任何作用)替换为伪参数 -
.应该很清楚,就像执行:
g(){ echo "once";};g;g
所以你会看到:
once
once
补救:不要使用参数!只需使用:
[alias]
encrypt-for = "!g(){ echo "once";};"
现在,如果您真的要使用参数,请确保根本不执行给定的尾随参数。一种可能是像这样添加尾随注释字符:
[alias]
encrypt-for = "!g(){ echo "once";}; #"
对于您的完整示例,更简洁的方法还可以是将所有内容包装在一个函数中:
[alias]
encrypt-for = "!main() {\
case in \
(github) echo github;; \
(twitter) echo twitter;; \
(facebook) echo facebook;; \
(*) echo >&2 \"error, unknown "\; exit 1;; \
esac \
}; main"
希望您了解 git
使用别名的幕后黑手!它实际上将 "$@"
附加到别名字符串,并使用此字符串和给定的参数调用 sh -c
。
这个问题已经被 gniourf_gniourf so I have created a version of the simplified alias/script which works as I originally intended. Since this is technically an answer and not really part of the question, I have added this as an answer. This answer supplements the other answer by gniourf_gniourf 回答了,并不是要剥夺他正确答案的功劳。
此简化脚本的固定版本要么执行找到的函数,要么根本不输出任何内容,Git 将 $@
放在脚本末尾的事实已由在脚本末尾添加注释。这是简化脚本的固定版本(它给出了执行一次的正确执行行为):
g(){
echo "once";
};
if [[ $(type -t "") == "function" ]];
then
;
fi;
#
这是此简化版 alias/script 的更正版本的输出( 具有正确的行为:执行一次并且对于未知输入不显示任何内容):
$git config --global alias.encrypt-for '!g(){ echo "once";};if [[ $(type -t "") == "function" ]];then ; fi;#'
$ git encrypt-for g
once
$ git encrypt-for github
$ git encrypt-for facebook
$ exit
最重要的是,由于 Git 处理别名的方式(参见 答案的解释),您必须解决 $@
将作为后缀的事实alias/script.
结束
如果我没有明确调用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
#!/usr/bin/env bash
github(){
echo github;
};
twitter(){
echo twitter;
};
facebook(){
echo facebook;
};
if [[ $(type -t "") == "function" ]];
then
"";
else
echo "There is no defined function for ";
fi;
但是这个稍微修改过的脚本按预期执行(运行函数只执行一次):
#!/usr/bin/env bash
github(){
echo github;
};
twitter(){
echo twitter;
};
facebook(){
echo facebook;
};
if [[ $(type -t "") == "function" ]];
then
"";
exit 0;
else
echo "There is no defined function for ";
exit 1;
fi;
这正是我通过 git 别名 运行 这些脚本时发生的事情(添加 set
命令仅用于调试目的 ):
$ git config --global alias.encrypt-for '!set -evu -o pipefail;github(){ echo github;};twitter(){ echo twitter;};facebook(){ echo facebook;};if [[ $(type -t "") == "function" ]];then ""; exit 0; else echo "There is no defined function for "; exit 1; fi;'
$ git encrypt-for "github"
type -t ""
github
$ git config --global alias.encrypt-for '!set -evu -o pipefail;github(){ echo github;};twitter(){ echo twitter;};facebook(){ echo facebook;};if [[ $(type -t "") == "function" ]];then ""; else echo "There is no defined function for "; fi;'
$ git encrypt-for "github"
type -t ""
github
github
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
将 echo github
替换为 echo "I am echo in github"
作为排除 echo
命令作为第二个函数执行源的方式的输出:
$ 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
以下是 alias/script 的最简单版本,它给出了双重执行的不良行为:
g(){
echo "once";
};
;
这是执行简化的 alias/script 的结果输出( 执行两次 的行为不正确):
$ git config --global alias.encrypt-for '!g(){ echo "once";};;'
$ git encrypt-for g
once
once
这是因为 git
处理别名的方式:
给定别名
[alias] myalias = !string
其中 <em>string</em>
是表示某些代码的任何字符串,当调用 git myalias <em>args</em>
其中 <em>args</em>
是一个(可能为空的)参数列表,git
将执行:
sh -c 'string "$@"' 'string' <em>args</em>
例如:
[alias]
banana = !echo ",,SNIP "
并打电话
git banana one 'two two' three
git
将执行:
sh -c 'echo ",,SNIP " "$@"' 'echo ",,SNIP "' one 'two two' three
所以输出将是:
one,two two,SNIP one two two three
在你的情况下,
[alias]
encrypt-for = "!g(){ echo \"once\";};;"
并打电话
git encrypt-for g
git
将执行:
sh -c 'g(){ echo "once";};;"$@"' 'g(){ echo "once";};;' g
为清楚起见,让我以等效形式重写:
sh -c 'g(){ echo "once";};;"$@"' - g
我只将 'g(){ echo "once";};;'
部分(将是 sh
的 [=29=]
的位置参数,在这里不会发挥任何作用)替换为伪参数 -
.应该很清楚,就像执行:
g(){ echo "once";};g;g
所以你会看到:
once
once
补救:不要使用参数!只需使用:
[alias]
encrypt-for = "!g(){ echo "once";};"
现在,如果您真的要使用参数,请确保根本不执行给定的尾随参数。一种可能是像这样添加尾随注释字符:
[alias]
encrypt-for = "!g(){ echo "once";}; #"
对于您的完整示例,更简洁的方法还可以是将所有内容包装在一个函数中:
[alias]
encrypt-for = "!main() {\
case in \
(github) echo github;; \
(twitter) echo twitter;; \
(facebook) echo facebook;; \
(*) echo >&2 \"error, unknown "\; exit 1;; \
esac \
}; main"
希望您了解 git
使用别名的幕后黑手!它实际上将 "$@"
附加到别名字符串,并使用此字符串和给定的参数调用 sh -c
。
这个问题已经被 gniourf_gniourf so I have created a version of the simplified alias/script which works as I originally intended. Since this is technically an answer and not really part of the question, I have added this as an answer. This answer supplements the other answer by gniourf_gniourf 回答了,并不是要剥夺他正确答案的功劳。
此简化脚本的固定版本要么执行找到的函数,要么根本不输出任何内容,Git 将 $@
放在脚本末尾的事实已由在脚本末尾添加注释。这是简化脚本的固定版本(它给出了执行一次的正确执行行为):
g(){
echo "once";
};
if [[ $(type -t "") == "function" ]];
then
;
fi;
#
这是此简化版 alias/script 的更正版本的输出( 具有正确的行为:执行一次并且对于未知输入不显示任何内容):
$git config --global alias.encrypt-for '!g(){ echo "once";};if [[ $(type -t "") == "function" ]];then ; fi;#'
$ git encrypt-for g
once
$ git encrypt-for github
$ git encrypt-for facebook
$ exit
最重要的是,由于 Git 处理别名的方式(参见 $@
将作为后缀的事实alias/script.