Заполнение файла 0xFF дает C3BF в OSX

977
Synesso

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

2 ответа на вопрос

9
JakeGould

Прямо к сути.

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

Подробнее

Похоже, что trв macOS конвертирует в 0xffUTF8 эквивалент, 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 
«В Linux для этого параметра установлено значение` C`, в то время как в macOS установлено что-то вроде `en_US.UTF-8`" - я не уверен, что это вся история. В моем Kubuntu или Debian `env | grep -E 'LANG | LC'` возвращает только `LANG = pl_PL.UTF-8`, так что это Unicode. Тем не менее, оригинальная команда OP выдает `0xff` из коробки. Может быть, потому что сама реализация tr отличается от Linux и Mac? Kamil Maciorowski 5 лет назад 4
Что касается моих сомнений, я нашел [этот ответ] (https://unix.stackexchange.com/a/165806/108618), в котором говорится, что «многие реализации` tr`, включая реализацию в GNU coreutils, не поддерживают многобайтовую кодировок». Выглядит вполне законно. В моем Debian `tr 'Ł' 'L'` переводит` Ł` в `LL` (` Ł` - польская буква, я использую `LANG = pl_PL.UTF-8`), поэтому он, очевидно, рассматривает свой первый аргумент как * два * символа. Kamil Maciorowski 5 лет назад 1
Да, это должен сделать `tr`. Такое преобразование будет иметь негативный смысл при записи в файл. grawity 5 лет назад 3
Нетрудно проверить, что дело не в настройках локали. С `LANG = en_US.UTF-8` (в системе Linux, в которой создан этот языковой стандарт),` printf '' | tr '' '\ 377' | hexdump -C` явно показывает `ff`. ilkkachu 5 лет назад 0
И, на самом деле, изменения `LANG` может быть недостаточно. Соответствующей настройкой локали является `LC_CTYPE`, и значение, которое она получает, приходит сначала из` LC_ALL`, затем `LC_CTYPE`, затем` LANG`, причем первый набор вступает в силу (то же самое для всех других настроек локали). Таким образом, если задано `LC_CTYPE`, изменение` LANG` ничего не даст в этом случае. Чтобы надежно переопределить его, вам нужно установить `LC_ALL`. Также достаточно установить его только для `tr`, т.е.` ... | LC_ALL = C tr '' '\ 377' | ... ` ilkkachu 5 лет назад 0
@ilkkachu Спасибо за советы! Изменения сделаны для улучшения ответа. Спасибо сообществу! JakeGould 5 лет назад 0
4
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 

В том же духе, смешивание an äи an öприводит к забавным результатам, так как их представления UTF-8 имеют общий байт:

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

Или наоборот (здесь xне применимо):

linux$ echo ab | tr ab äx ä 

И в вашем случае, GNU trпринимает значение \377необработанного байта.

На trMac отличается, он знает концепцию многобайтовых символов и действует соответственно:

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'