Why is this Bash function within a git alias executing twice, and why does adding `exit` fix it?

前端 未结 2 1836
星月不相逢
星月不相逢 2021-02-15 04:01

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

2条回答
  •  旧时难觅i
    2021-02-15 04:36

    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.

提交回复
热议问题