Кодування символів вашої мови (про яке ви можете розповісти 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
…