Warum gibt „echo -ne "commit $(git cat-file commit cc540cb | wc -c)\0"“ vor der Befehlsersetzung Leerzeichen aus?

Warum gibt „echo -ne "commit $(git cat-file commit cc540cb | wc -c)\0"“ vor der Befehlsersetzung Leerzeichen aus?

Um die Funktionsweise von Git kennenzulernen, wollte ich Commit-Hashes manuell berechnen, wie inDasAlso antworte.

Während es jedoch printffunktioniert (siehe unten), tritt bei mir ein merkwürdiges Verhalten auf echo, das vor der Befehlsersetzung Leerzeichen ausgibt.

Warum werden echodiese Leerzeichen in die Ausgabe eingebettet?

Falsch ( 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

Richtig ( 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

Antwort1

wc -cgibt Leerzeichen vor der Zahl aus:

$ wc -c <file
       6

In Ihrem ersten Befehl werden die Zeichenfolge commitund die Ausgabe wc -cin doppelten Anführungszeichen gehalten.

In Ihrem zweiten Befehl wird die Ausgabe wc -cohne Anführungszeichen verwendet.

In einem Kontext ohne Anführungszeichen würde die Shell die Ausgabe vor der Verwendung der resultierenden Wörter in die Zeichen in aufteilen $IFS, die ein Leerzeichen enthalten. (Sie würde auch Dateinamen-Globbing für die Wörter durchführen, aber das spielt hier keine Rolle.)

Sie würden mit Ihrem zweiten Befehl die gleiche Ausgabe erhalten, wenn Sie die Befehlsersetzung in Anführungszeichen setzen würden.

Auch wenn Sie meinen, der zweite Befehl mit printfsei „korrekt“, ist es aufgrund der von der Shell durchgeführten Aufteilung und Globbing im Allgemeinen keine gute Idee, eine Befehlsersetzung nicht in Anführungszeichen zu setzen.

Ich würde vorschlagen, die Befehlsersetzung in Anführungszeichen zu setzen (weil das immer eine gute Vorgehensweise ist) und stattdessen die Leerzeichen explizit aus der Ausgabe von wc -c: zu löschen:

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

verwandte Informationen