Кодировка символов вашей локали (которую вы можете сказать 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...