bash - 为什么 \x0d\x20 擦除该行

Fru*_*uit 6 grep bash terminal cat

这是来自 gedit 编辑器的视图: 在此处输入图片说明

和 vim 编辑器的视图: 在此处输入图片说明

然后我尝试 grep 它,如果我把 Log 而不是 Tog,它确实 grep 成功,但输出已损坏:

[xiaobai@xiaobai grep]$ grep  Tog test
[xiaobai@xiaobai grep]$ grep  Log test
                               Dtring.valueOf
[xiaobai@xiaobai grep]$ 
Run Code Online (Sandbox Code Playgroud)

然后我找到文件,它也损坏了:

[xiaobai@xiaobai grep]$ cat test 
                               Dtring.valueOf
[xiaobai@xiaobai grep]$ 
Run Code Online (Sandbox Code Playgroud)

所以我使用十六进制转储:

[xiaobai@xiaobai grep]$ hexdump -C test 
00000000  4c 6f 67 2e 64 28 22 6d  75 73 69 63 22 2c 20 22  |Log.d("music", "|
00000010  4e 41 56 49 47 41 54 4f  52 3a 20 22 20 2b 20 53  |NAVIGATOR: " + S|
00000020  74 72 69 6e 67 2e 76 61  6c 75 65 4f 66 0d 20 20  |tring.valueOf.  |
00000030  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
00000040  20 20 20 20 20 20 20 20  20 20 20 20 20 44 0d 0a  |             D..|
00000050
[xiaobai@xiaobai grep]$ 
Run Code Online (Sandbox Code Playgroud)

我缩小范围:

[xiaobai@xiaobai grep]$ cat test3
                               D
[xiaobai@xiaobai grep]$ hexdump -C test3
00000000  61 0d 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |a.              |
00000010  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
00000020  20 44 0d 0a                                       | D..|
00000024
[xiaobai@xiaobai grep]$ echo -e '\x61'
a
[xiaobai@xiaobai grep]$ echo -e '\x61\x0d'
a
[xiaobai@xiaobai grep]$ echo -e '\x61\x0d\x20'

[xiaobai@xiaobai grep]$ echo -e '\x61\x0d\x20\x62'
 b
Run Code Online (Sandbox Code Playgroud)

如您所见,“a”在我添加一个 \x20 字节后被删除。

所以我的问题是,为什么会发生这种情况,我如何在不知道某些文件可能包含 \x0d\x20 的情况下摆脱这种情况,例如 grep -r ?

Sté*_*las 14

ASCII 码 0 到 31 的字符是控制字符。当发送到终端时,它们被用来做特殊的事情。例如,\a(BEL, 0x7) 使终端的铃响。\b(BS, 0x8) 向后移动光标。\n(LF, 0xa) 将光标向下移动一行,\t(TAB 0x9) 将光标移动到下一个表格...

\r (CR, 0xd) 将光标移动到第一列。

当您在终端中的 shell 提示符下运行时:

printf 'foo\nbar\n'
Run Code Online (Sandbox Code Playgroud)

printf写入foo\nbar\n/dev/tty<something>,该设备的TTY线路规程转换,要foo\r\nbar\r\n,这就是为什么你看到bar后的下一行foo

printf 'foo\rbar\n'
Run Code Online (Sandbox Code Playgroud)

会让终端foobar.

如果您的文件包含控制字符,如果您想检查它们的存在,您可以删除它们,或者给它们一个文本表示(例如^M\rCR 0xd 字符)。

不过,您可能不想对 LF 和 TAB 字符执行此操作。所以:

LC_ALL=C tr -d '\0-\10\13-\37\177' < file # to remove them

cat -v < file # to display as ^M

sed -n l < file # to display as \r (also converts TAB to \t)
                # and marks the end of lines with $
Run Code Online (Sandbox Code Playgroud)

请注意,那些sedcat那些也将转换非 ASCII 字符。你可以这样做:

LC_ALL=C sed "$(printf 's/[^\t -\176\200-\377]/^&/g')" < file |
  LC_ALL=C tr '\0-\10\13-\37\177' '@-HK-_?'
Run Code Online (Sandbox Code Playgroud)

仅将 ASCII 控制字符(TAB 和 LF 除外)转换为其^X可视形式(请注意,并非所有sed实现都支持其中包含 NUL 字符的输入文件)。