Зразок команди, що проявляє симптом: sed 's/./@/' <<<$'\xfc'
не вдається, оскільки байт 0xfc
не є дійсним знаком UTF-8.
Зауважте, що, навпаки, GNU sed
(Linux, але також встановлений на macOS) просто передає недійсний байт, не повідомляючи про помилку.
Використання раніше прийнятої відповіді - це варіант, якщо ви не проти втратити підтримку свого справжнього локалу (якщо ви користуєтесь системою США і вам ніколи не потрібно мати справу з іноземними символами, це може бути добре).
Однак той же ефект можна отримати Ad-Hoc для однієї команди тільки :
LC_ALL=C sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure
Примітка: Що важливо, це ефективне LC_CTYPE
налаштування C
, так LC_CTYPE=C sed ...
би зазвичай працювало, але якщо LC_ALL
трапиться встановити (на щось інше, ніж C
), воно буде заміняти окремі LC_*
змінні категорії категорії, такі як LC_CTYPE
. Таким чином, встановити найбільш стійкий підхід LC_ALL
.
Тим НЕ менше, (фактично) настройки LC_CTYPE
для C
обробляють рядки , як якщо б кожен байт був свій характер ( НЕ інтерпретації , заснована на правилах кодування виконується), причому без урахування для - мультибайтних на вимогу - UTF-8 , що кодують , що OS X використовує за замовчуванням , де іноземні символи мають багатобайтові кодування .
У двох словах: заходять LC_CTYPE
наC
причини оболонку і утиліти тільки розпізнавати основні англійські букви як літери (ті , в 7-бітному діапазоні ASCII), так що іноземні Лису гору. не розглядатиметься як букви , що призведе, наприклад, до великих та малих перетворень.
Знову ж, це може бути нормально, якщо вам не потрібно відповідати багатобайтовим символам, таким як é
, і просто хочете передати такі символи .
Якщо цього недостатньо і / або ви хочете зрозуміти причину початкової помилки (включаючи визначення того, які вхідні байти спричинили проблему) та виконайте кодування перетворень на вимогу, читайте нижче.
Проблема полягає в тому, що кодування вхідного файлу не відповідає оболонці.
Більш конкретно, вхідний файл містить символи, закодовані таким чином, що не відповідають дійсності в UTF-8 (як @Klas Lindbäck зазначив у коментарі) - ось що sed
намагається сказати повідомлення про помилку invalid byte sequence
.
Швидше за все, ваш вхідний файл використовує однобайтове 8-бітове кодування, таке як ISO-8859-1
, часто використовується для кодування "західноєвропейських" мов.
Приклад:
Лист з наголосом à
має кодову точку Unicode 0xE0
(224) - те саме, що і в ISO-8859-1
. Однак, через характер кодування UTF-8 , ця єдина кодова точка представлена у вигляді 2-х байт - 0xC3 0xA0
, тоді як спроба передати один байт 0xE0
є недійсною для UTF-8.
Ось демонстрація проблеми за допомогою рядка, voilà
кодованого як ISO-8859-1
, із à
представленим як один байт (через ANSI-C, цитовану рядок bash ( $'...'
), який використовується \x{e0}
для створення байта):
Зауважте, що sed
команда фактично є неоперативною, яка просто передає вхід, але нам це потрібно, щоб спровокувати помилку:
# -> 'illegal byte sequence': byte 0xE0 is not a valid char.
sed 's/.*/&/' <<<$'voil\x{e0}'
Щоб просто ігнорувати проблему , LCTYPE=C
можна використати наведений вище підхід:
# No error, bytes are passed through ('á' will render as '?', though).
LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}'
Якщо ви хочете визначити, які частини вводу викликають проблему , спробуйте наступне:
# Convert bytes in the 8-bit range (high bit set) to hex. representation.
# -> 'voil\x{e0}'
iconv -f ASCII --byte-subst='\x{%02x}' <<<$'voil\x{e0}'
Вихід покаже вам всі байти, у яких встановлено високий біт (у байтах, що перевищують 7-бітовий діапазон ASCII) у шістнадцятковій формі. (Однак зауважте, що це також включає в себе правильно закодовані багатобайтові послідовності UTF-8 - потрібен буде більш складний підхід, щоб спеціально ідентифікувати недійсні байти в UTF-8.)
Виконання кодування перетворень на вимогу :
Стандартну утиліту iconv
можна використовувати для перетворення в ( -t
) та / або з ( -f
) кодувань; iconv -l
перелічує всі підтримувані.
Приклади:
Перетворити ВІД ISO-8859-1
у діюче кодування в оболонці (засноване на LC_CTYPE
, яке UTF-8
за замовчуванням є базовим), спираючись на наведений вище приклад:
# Converts to UTF-8; output renders correctly as 'voilà'
sed 's/.*/&/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')"
Зауважте, що ця конверсія дозволяє правильно відповідати іноземним символам :
# Correctly matches 'à' and replaces it with 'ü': -> 'voilü'
sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')"
Щоб перетворити вхідний BACK в ISO-8859-1
обробку, просто передайте результат іншій iconv
команді:
sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')" | iconv -t ISO-8859-1