为什么引用参数在git别名中不起作用?

时间:2019-05-07 03:28:35

标签: git shell parsing

我有以下~/.gitconfig

[alias]
    print = !printf %s\\\\n a b c

其行为如下:

$ git print
a
b
c

但是现在我将其更改为以下~/.gitconfig

[alias]
    print = !printf %s\\\\n "a b" c

,但是ab仍然会进行分词:

$ git print
a
b
c

如果""引号不能阻止单词拆分,那么它们在做什么?
命令如何解析?

1 个答案:

答案 0 :(得分:5)

Git的配置语言解析器uses double quotes for its own purposes

  

...通过将双引号"\"分别转义,可以包含双引号\\和反斜杠。阅读时,其他字符前面的反斜杠将被删除;例如,\t读为t,而\0读为0 ...

尽管本段讨论的是小节名称,但双引号和双反斜杠规则也适用于小节名称之外。这些引号的其余部分适用于所有地方,但小节名称中的\t = t类除外:

  

必须在双引号,双引号"和反斜杠\中转义:\"使用"\\使用\

     

可以识别以下转义序列(在\"\\旁):\n用于换行符(NL),\t用于水平制表(HT,TAB)和\b用于退格(BS)。其他字符转义序列(包括八进制转义序列)无效。

请注意,其中的都与别名无关。这些规则适用于.gitconfig.git/config文件中的 all 行。 (这在例如子模块路径中很重要,尽管通常不会编码用双引号引起来。)

现在,之后配置解析器已完成读取.git/config文件的操作,如果该行位于alias节中,则它定义了一个别名。在该别名中,双引号确实可以防止单词拆分:

[alias]
    foo = log \"a b\"

$ git foo
fatal: ambiguous argument 'a b': unknown revision or path not in the working tree.

请注意,首先要通过配置解析器,必须使用反斜杠。为了避免这种烦恼,我们可以改用单引号:

[alias]
    foo = log 'a b'

产生相同的结果。

如果别名是 shell 别名(以!为前缀),则整个字符串(当然要减去感叹号)都输入到shell中。要观察这一点,请使用GIT_TRACE

[alias]
    foo = !printf %s\\\\n 'a b'

$ GIT_TRACE=1 git foo
23:15:46.947801 git.c:670               trace: exec: git-foo
23:15:46.948153 run-command.c:643       trace: run_command: git-foo
23:15:46.948883 run-command.c:643       trace: run_command: 'printf %s\\n '\''a b'\'''
a b

如果我们在配置解析器后得到双引号,则它们具有相同的效果:

$ GIT_TRACE=1 git foo
23:16:24.919042 git.c:670               trace: exec: git-foo
23:16:24.919402 run-command.c:643       trace: run_command: git-foo
23:16:24.920114 run-command.c:643       trace: run_command: 'printf %s\\n "a b"'
a b

我们之所以需要四个反斜杠,是因为Git配置解析器将它们变成了两个反斜杠(如GIT_TRACE=1输出所示),然后shell本身将它们变成了一个反斜杠,{{{ 1}}和printf组合成换行符。

尝试使用别名:

n

失败:

    foo = !printf %s\\\\n "a b

因为Git本身正在运行逐行解析器,并且对未封闭的报价不满意。