需要 POSIX 解决方案来从文件中删除特殊的不可见字符

需要 POSIX 解决方案来从文件中删除特殊的不可见字符

我有一个由第三方(Windows)提供的文件,它不受我们控制。

我观察到它有如下特殊字符:

root@DKERP:~# cat -ev ~/check_disk_space.sh
M-oM-;M-?#!/bin/bash^M$
^M$
# Arguments: <mount_points> <min_free_space>^M$
^M$

我尝试了下面的解决方案,它删除了所有特殊字符,但是M-oM-;M-?

sed -i -e 's/\r//g; s/'$'\302''//g; s/'$'\273''//g' ~/check_disk_space.sh

您能建议我如何删除所有特殊字符吗?

答案1

总结

跳至名为“通用解决方案sed”的部分。


解释

M-oM-;M-?是如何cat -ev向你展示UTF-8 的 BOM

看起来有问题的文件应该是 shell 脚本。Linux 中的脚本应采用 UTF-8 编码,不带 BOM,且行以\n( LF$在 的输出中cat -ev) 结尾,而不是以\r\n( CRLF^M$在 的输出中cat -ev) 结尾。您绝对应该删除这些\r字符和 BOM。

dos2unix是一个转换换行符的工具,它还可以删除 BOM。但它不是一个可移植的工具,您的 Linux 中可能有它,也可能没有。

麻烦的BOM是 (十六进制) EF BB BF,以八进制表示则是357 273 277

您的解决方案sed尝试删除273;如果它有效,它应该删除M-;。如果它有效,那么添加357277将很简单。但是您使用的命令不符合 POSIX 标准,至少有两个原因:


简单(但不完美)的解决方案tr

一个 POSIX 工具,用于删除字节(理论上是字符,但是并非总是如此) 是tr -d。它支持八进制表示,因此您不需要 shell 进行扩展$'\273'等。命令如下:

<source_file tr -d '\r\357\273\277' >destination_file

警告:不要将输出重定向到source_file

我不清楚你为什么要删除八进制表示的字节302\302如果你认为需要的话,请将其添加到上面的命令中。

不幸的是,我们想要删除的字节中至少有一些可能出现在 BOM 之后。例如,字符在 UTF-8 中»是(八进制) 。(也许您的设计就是为了删除这个确切的字符?)302 273sed


另一种简单(但仍然有些缺陷)的解决方案是dd

为了避免删除非 BOM 的匹配字节,请考虑采用其他方法。您可以跳过前三个字节,无论它们是什么:

<source_file dd bs=1 skip=3 >destination_file

\r您也想删除,因此完整命令将是:

<source_file dd bs=1 skip=3 | tr -d '\r' >destination_file

即使没有 BOM,我们的原始版本tr也应该可以正常工作,但如果文件中有奇特的字符(如»),则该工具可能会因删除过多而造成损害。另一方面,我们dd只会删除前三个字节,因此它不会触及文件中后面的字节,但如果没有三字节 BOM,它会造成损害。

如果你确定 UTF-8 有 BOM,那么使用这个解决方案dd就可以了。


通用解决方案sed

通用的解决方案如下:

<source_file sed "
1 s|^$(printf '\357\273\277')||;
  s|$(printf '\r')||g;
" >destination_file

它消除了精确的仅从第一行开头开始 BOM(如果存在 BOM);并且它不会触及»可能在后面出现的类似字符。它还会删除所有回车符。请注意,在 UTF-8 中,我们表示的字节\r不能作为多字节字符的一部分出现,因此它不会出现与 类似的问题\273

如果您sed支持,-i则使用它(sed -i… "…" source_file)您就完成了。答案继续,因为您请求了 POSIX 解决方案,即没有sed -i


做的工作sed -i

使用trdd+tr或之后sed,您可以

mv destination_file source_file

sed -i会在后台执行此操作(或类似操作)。请注意,您的(以及之后的destination_file新)拥有与旧无关的所有权和模式(权限),您可能需要更正它们。为避免此问题,请尝试使用而不是:source_filemvsource_filecatmv

<destination_file cat >source_file && rm destination_file

写回cat不是原子的(在某种意义上mv在单个文件系统中很可能是原子的),但它不会改变你的 的所有权或模式source_file。当然,你需要对 有写权限才能做到source_file这一点;所以如果所有权和模式对你来说不合适(你无法写入文件)那么 就cat行不通了,mv也许。不管怎样,最终你可能(或可能不需要)chmod和/或chown

请注意,我们的mv或我们的重定向cat >…会破坏源数据。只有在您确定正确后,才应运行一个异或另一个destination_file。使用链接命令&&可能会有所帮助。示例:

<source_file sed "
1 s|^$(printf '\357\273\277')||;
  s|$(printf '\r')||g;
" >destination_file \
&& <destination_file cat >source_file \
&& rm destination_file

相关内容