題 使用0xFF填充文件會在OSX中提供C3BF


此命令將填充文件 0xff 在Linux中。

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

這裡發生了什麼?


4
2017-08-16 03:35


起源




答案:


開門見山。

這一切都取決於 LANG 要么 LC_ALL 運行時在終端會話中設置的值 tr。 Linux將它們設置為 C 雖然macOS將它設置為類似的東西 en_US.UTF-8。當然那樣 en_US 可能是其他一些當地語言,如 en_UK (英國英語)但重點是 [something].UTF-8 設置而不是普通的ASCII通過 C 是造成這種情況的原因。

更多細節。

似乎是這樣 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

9
2017-08-16 04:00



“Linux就是這樣的 C 雖然macOS將它設置為類似的東西 en_US.UTF-8“ - 我不確定這是完整的故事。在我的Kubuntu或Debian中 env | grep -E 'LANG|LC' 回報 LANG=pl_PL.UTF-8 只是,所以它是Unicode。 OP的原始命令仍然產生 0xff 盒子外面。可能是因為 trLinux和Mac之間的實現本身有何不同? - Kamil Maciorowski
關於我的疑問,我發現了 這個答案 其中說“許多實施 tr,包括GNU coreutils中的那個,不支持多字節編碼“。似乎合法。在我的Debian中 tr 'Ł' 'L' 轉換 Ł 至 LL (Ł 我用的是波蘭語 LANG=pl_PL.UTF-8),所以它顯然將其第一個論點視為 二 字符。 - Kamil Maciorowski
是的,它必須通過 tr。在寫入文件時,這種轉換會產生負面意義。 - grawity
測試它不是關於語言環境設置並不是很難。同 LANG=en_US.UTF-8 (在生成該語言環境的Linux系統上), printf ' ' | tr ' ' '\377' | hexdump -C 清楚地表明 ff。 - ilkkachu
實際上,改變了 LANG 可能還不夠。相關的區域設置是 LC_CTYPE,它獲得的價值首先來自 LC_ALL, 然後 LC_CTYPE, 然後 LANG,第一組生效(所有其他語言環境設置都相同)。因此,如果 LC_CTYPE 改變了 LANG 在這種情況下不做任何事情。要可靠地覆蓋它,您需要設置 LC_ALL。此外,它足以設置它 tr,即 ... | LC_ALL=C tr ' ' '\377' | ... - ilkkachu


問題是GNU tr,你在Linux上,並沒有真正的多字節字符的概念,而是一次工作字節。

tr 手冊頁 和在線文檔談論字符,但這是一個簡化。該 TODO 源代碼包中的文件提到了這個項目(摘自 coreutils 8.30):

適應wc,tr,fmt等工具(大多數textutils)   多字節意識。問題是我想避免重複   重要的邏輯塊,但我也想要產生最小的邏輯   (在單字節模式下操作時)(優選“否”)成本。

在Linux系統上 - 即使使用UTF-8語言環境(en_US.UTF-8)-GNU tr 替換一個 ä 作為兩個“字符”(UTF-8表示的 ä 有兩個字節):

linux$ echo 'ä' | tr 'ä' 'x'
xx

以同樣的方式,混合一個 ä 和 ö 產生有趣的結果,因為他們的UTF-8表示共享一個公共字節:

linux$ echo 'ö' | tr ä x
x�

或者相反( x 這裡不適用):

linux$ echo ab | tr ab äx
ä

在你的情況下,GNU tr 拿走了 \377 作為原始字節值。

tr在Mac上是不同的,它知道多字節字符的概念並相應地採取行動:

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'

4
2017-08-16 19:41