Кодировка символов вашей локали (которую вы можете сказать locale charmap
) - многобайтовая на символ.
Наиболее распространенным в настоящее время является UTF-8, где символы могут быть закодированы от 1 до 4 байтов. Не все последовательности байтов образуют допустимые символы в UTF-8. Каждый не ASCII-символ в UTF-8 начинается с одного байта, в котором установлены два старших бита, и сообщает, сколько байтов следует за старшим (но не вторым старшим) битом.
/dev/urandom
содержит случайный поток байтов. tr
транслитерирует символ, поэтому он должен декодировать эти байты как символы. Все символы ASCII в вашем диапазоне кодируются одним символом в UTF-8, но tr
все же необходимо декодировать все символы. Есть, например, другие многобайтовые кодировки, где некоторые символы, кроме, A
содержат байт 0x41 (код для A
).
Поскольку этот случайный поток байтов должен содержать недопустимые последовательности (например, сам по себе байт 0x80 является недопустимым в UTF-8, так как не-ASCII-символ должен начинаться с байта, большего чем 0xc1 (0xc0 и 0xc1 отсутствуют в UTF- 8 символов)), поэтому tr
возвращается с ошибкой, когда это происходит.
Здесь вы хотите рассмотреть этот поток байтов как символы в кодировке, которая имеет один байт на символ. Какой бы вы выбрали, не важно , так как все эти символы в вашем диапазоне (предполагая , что от AZ, вы имели в виду ABCDEFGHIJKLMNOPQRSTUVWXYZ и не такие вещи , как Ý
, Ê
) являются частью портативного набора символов так быть закодированы то же самое во всех кодировок , поддерживаемых в системе.
Для этого, нужно установить LC_CTYPE
переменную локализации , которая является тот , который решает , какой набор символов используется и то , что такие вещи , как blank
, alpha
символьные классы содержат. Но для определения диапазона AZ вы также захотите установить LC_COLLATE
переменную (ту, которая решает порядок строк).
C
Ака POSIX
локаль одно , что гарантирует символы в одиночные байты и AZ является АБВГДЕЖЗИКЛМНОПРСТУФХЧШЭЮЯ. Вы могли бы сделать:
LC_CTYPE=C LC_COLLATE=C tr -dc 'A-Za-z0-9_!@#$%^&*()+=-'
(здесь перемещение -
до конца, в противном случае, )-+
будет восприниматься как диапазон A-Z
)
Но обратите внимание, что LC_ALL
переменная переопределяет все остальные LC_*
и LANG
переменные. Таким образом, если LC_ALL
иное уже определено, вышеприведенное не будет иметь никакого эффекта. Так что вместо этого вы можете просто сделать:
LC_ALL=C tr -dc 'A-Za-z0-9_!@#$%^&*()+=-'
Это повлияет на другие вещи, такие как язык сообщений об ошибках, но в любом случае изменение LC_CTYPE уже могло быть проблемой для сообщений об ошибках (например, нет способа выразить русские или японские сообщения об ошибках в кодировке локали C).
xargs
...