我有以下~/.gitconfig
:
[alias]
print = !printf %s\\\\n a b c
其行为如下:
$ git print
a
b
c
但是现在我将其更改为以下~/.gitconfig
:
[alias]
print = !printf %s\\\\n "a b" c
,但是a
和b
仍然会进行分词:
$ git print
a
b
c
如果""
引号不能阻止单词拆分,那么它们在做什么?
命令如何解析?
答案 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本身正在运行逐行解析器,并且对未封闭的报价不满意。