为什么 `echo -ne "commit $(git cat-file commit cc540cb | wc -c)\0"` 在命令替换之前输出空格?

为什么 `echo -ne "commit $(git cat-file commit cc540cb | wc -c)\0"` 在命令替换之前输出空格?

为了学习 Git 的内部工作原理,我想手动计算提交哈希,如下所述所以回答。

然而,虽然printf有效(见下文),但我得到了奇怪的行为echo,它在命令替换之前输出空格。

为什么echoput 在输出中嵌入这些空格?

错误的是( echo)

$ (echo -ne "commit $(git cat-file commit cc540cb | wc -c)\0"; git cat-file commit cc540cb) | hexdump -Cv
00000000  63 6f 6d 6d 69 74 20 20  20 20 20 20 32 31 38 00  |commit      218.|
00000010  74 72 65 65 20 34 35 64  38 62 30 35 38 32 65 32  |tree 45d8b0582e2|
00000020  34 62 39 61 66 66 38 62  34 64 34 65 35 66 33 31  |4b9aff8b4d4e5f31|
00000030  65 35 38 64 62 38 37 38  61 33 64 32 35 0a 70 61  |e58db878a3d25.pa|
00000040  72 65 6e 74 20 64 64 31  37 31 64 61 35 62 61 64  |rent dd171da5bad|
00000050  31 34 62 36 30 37 36 65  64 64 36 30 66 35 38 33  |14b6076edd60f583|
00000060  33 63 39 33 63 65 38 33  61 36 66 64 61 0a 61 75  |3c93ce83a6fda.au|
00000070  74 68 6f 72 20 6e 6c 79  6b 6b 65 69 20 3c 6e 6c  |thor nlykkei <nl|
00000080  79 6b 6b 65 69 40 67 6d  61 69 6c 2e 63 6f 6d 3e  |[email protected]>|
00000090  20 31 35 38 36 37 30 33  39 31 39 20 2b 30 32 30  | 1586703919 +020|
000000a0  30 0a 63 6f 6d 6d 69 74  74 65 72 20 6e 6c 79 6b  |0.committer nlyk|
000000b0  6b 65 69 20 3c 6e 6c 79  6b 6b 65 69 40 67 6d 61  |kei <nlykkei@gma|
000000c0  69 6c 2e 63 6f 6d 3e 20  31 35 38 36 37 30 37 34  |il.com> 15867074|
000000d0  35 38 20 2b 30 32 30 30  0a 0a 41 77 65 73 6f 6d  |58 +0200..Awesom|
000000e0  65 20 72 65 62 61 73 65  21 0a                    |e rebase!.|
000000ea

正确的 (printf

$ (printf "commit %s\0" $(git cat-file commit cc540cb | wc -c); git cat-file commit cc540cb) | hexdump -Cv
00000000  63 6f 6d 6d 69 74 20 32  31 38 00 74 72 65 65 20  |commit 218.tree |
00000010  34 35 64 38 62 30 35 38  32 65 32 34 62 39 61 66  |45d8b0582e24b9af|
00000020  66 38 62 34 64 34 65 35  66 33 31 65 35 38 64 62  |f8b4d4e5f31e58db|
00000030  38 37 38 61 33 64 32 35  0a 70 61 72 65 6e 74 20  |878a3d25.parent |
00000040  64 64 31 37 31 64 61 35  62 61 64 31 34 62 36 30  |dd171da5bad14b60|
00000050  37 36 65 64 64 36 30 66  35 38 33 33 63 39 33 63  |76edd60f5833c93c|
00000060  65 38 33 61 36 66 64 61  0a 61 75 74 68 6f 72 20  |e83a6fda.author |
00000070  6e 6c 79 6b 6b 65 69 20  3c 6e 6c 79 6b 6b 65 69  |nlykkei <nlykkei|
00000080  40 67 6d 61 69 6c 2e 63  6f 6d 3e 20 31 35 38 36  |@gmail.com> 1586|
00000090  37 30 33 39 31 39 20 2b  30 32 30 30 0a 63 6f 6d  |703919 +0200.com|
000000a0  6d 69 74 74 65 72 20 6e  6c 79 6b 6b 65 69 20 3c  |mitter nlykkei <|
000000b0  6e 6c 79 6b 6b 65 69 40  67 6d 61 69 6c 2e 63 6f  |[email protected]|
000000c0  6d 3e 20 31 35 38 36 37  30 37 34 35 38 20 2b 30  |m> 1586707458 +0|
000000d0  32 30 30 0a 0a 41 77 65  73 6f 6d 65 20 72 65 62  |200..Awesome reb|
000000e0  61 73 65 21 0a                                    |ase!.|
000000e5

答案1

wc -c在数字之前输出空格:

$ wc -c <file
       6

在第一个命令中,字符串commit和输出wc -c保存在双引号字符串内。

在第二个命令中, 的输出wc -c使用不带引号的。

在不带引号的上下文中,shell 会$IFS在使用结果单词之前将输出拆分为 中的字符(其中包括空格)。 (它也会对单词进行文件名通配,但这在这里并不重要)。

如果引用命令替换,您将获得与第二个命令相同的输出。

尽管您认为第二个命令 withprintf是“正确的”,但由于 shell 会进行分割和通配,因此不引用命令替换通常是一个坏主意。

我建议引用命令替换(因为这始终是一个很好的做法),并从以下输出中显式删除空格wc -c

(printf "commit %s\0" "$(git cat-file commit cc540cb | wc -c | tr -d ' ')"; git cat-file commit cc540cb) | hexdump -Cv

相关内容