4

Эта команда заполнит файл 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

Что тут происходит?

2 ответа2

9

Прямо к сути.

Все зависит от значения LANG или LC_ALL установленного в сеансе терминала при запуске tr. В Linux для них установлено значение C а для macOS - что-то вроде en_US.UTF-8 . Конечно, en_US может быть другим локальным языком, например en_UK (английский английский), но дело в том [something].UTF-8 настройка UTF-8 вместо простого ASCII через C

Подробнее

Кажется, что tr в macOS преобразует 0xff в UTF8, эквивалентный c3bf когда он получает вместо чистого ASCII 0xff . Это объясняется здесь, в этой ветке поддержки сообщества Apple здесь:

Linux не обрабатывает Unicode в терминале, как Mac. Если вы установите переменную среды "LANG" в "C" (как это, вероятно, в Linux), она будет работать. В противном случае все эти старшие биты будут интерпретироваться как символы Юникода.

И использование этой подсказки 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
4

Проблема в том, что 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

UTF-8 представление символа с числовым значением 0377 (U+00ff) - это два байта 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'

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .