此命令将在Linux中填充文件0xff
。
dd if=/dev/zero ibs=1k count=100 | tr "\000" "\377" >paddedFile.bin
当我在 OSX 中运行它时,结果不同。
$ dd if=/dev/zero ibs=1k count=100 | tr "\000" "\377" >paddedFile.bin
100+0 records in
200+0 records out
102400 bytes transferred in 0.000781 secs (131104008 bytes/sec)
$ hexdump -C paddedFile.bin
00000000 c3 bf c3 bf c3 bf c3 bf c3 bf c3 bf c3 bf c3 bf
|................|
*
00032000
这里发生了什么?
答案1
开门见山。
这一切都取决于你运行终端会话时设置的LANG
或值LC_ALL
tr
。Linux 将它们设置为,C
而 macOS 则将其设置为类似en_US.UTF-8
。当然,这en_US
可能是其他本地语言,例如en_UK
(英国英语),但关键是[something].UTF-8
设置而不是纯 ASCII viaC
是导致这种情况的原因。
更多细节。
似乎tr
在 macOS 中,当获取 时,会将 转换0xff
为 UTF8 等效的 ,c3bf
而不是纯 ASCII 0xff
。 此处对此进行了解释此处为 Apple 社区支持主题:
Linux 不像 Mac 那样在终端中处理 Unicode。如果您将“LANG”环境变量设置为“C”(Linux 上可能如此),它就会正常工作。否则,所有这些高位都将被解释为 Unicode 字符。
并且使用该LANG
技巧有效!只需执行以下操作;我刚刚在 macOS 10.13.6 (High Sierra) 上进行了亲自测试。
首先,记下现有的LANG
值,如下所示:
echo $LANG
我看到的输出是:
en_US.UTF-8
现在将LANG
值设置为C
如下形式:
LANG=C
然后再次运行该命令:
dd if=/dev/zero ibs=1k count=100 | tr "\000" "\377" >paddedFile.bin
现在这些hexdump
值看起来应该是这样的:
hexdump -C paddedFile.bin
00000000 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
00019000
要重置该LANG
值,只需关闭该终端会话或运行此命令:
LANG=en_US.UTF-8
或者 - 正如评论中指出的那样 - 您可以LANG
在调用之前直接在命令行选项中设置该值,tr
如下所示:
dd if=/dev/zero ibs=1k count=100 | LANG=C tr "\000" "\377" >paddedFile.bin
你甚至可以使用LC_ALL
它来代替,LANG
因为LANG
它无论如何都是从这里派生出来的,LC_ALL
就像这样:
dd if=/dev/zero ibs=1k count=100 | LC_ALL=C tr "\000" "\377" >paddedFile.bin
答案2
问题是tr
,Linux 上的 GNU 实际上并没有多字节字符的概念,而是一次按字节工作。
这tr
手册页和在线文档都提到了字符,但这有点简单化。TODO
源代码包中的文件提到了此项(摘自coreutils 8.30):
调整 wc、tr、fmt 等工具(大多数文本工具)以支持多字节。问题是我想避免重复重要的逻辑块,同时还希望在单字节模式下运行时只产生最小(最好是“无”)成本。
在 Linux 系统上 — 即使使用 UTF-8 语言环境 ( en_US.UTF-8
) — GNUtr
也会将 替换ä
为两个“字符”( 的 UTF-8 表示形式ä
有两个字节):
linux$ echo 'ä' | tr 'ä' 'x'
xx
同样,将ä
和混合ö
也会产生有趣的结果,因为它们的 UTF-8 表示形式共享一个共同的字节:
linux$ echo 'ö' | tr ä x
x�
或者反过来(x
这里不适用):
linux$ echo ab | tr ab äx
ä
在您的情况下,GNUtr
将其\377
视为原始字节值。
Mac 上的则tr
不同,它知道多字节字符的概念并采取相应的行动:
mac$ echo 'ä' | tr ä x
x
mac$ echo ab | tr ab äx
äx
数值为 0377(U+00ff)的字符的 UTF-8 表示形式是两个字节c3 bf
,因此这就是您得到的。
tr
让逐字节工作最简单的方法是让它使用 C 语言环境,而不是 UTF-8 语言环境。这又产生了有趣的行为:
$ echo 'ä' | LC_ALL=C tr 'ä' 'x'
xx
就您而言,您可以使用:
... | LC_ALL=C tr "\000" "\377"
或者你可以使用像 Perl 之类的东西来生成这些\xff
字节:
perl -e 'printf "\377" x 1000 for 1..100'