Ответ 1
Пример команды с симптомом: sed 's/./@/' <<<$'\xfc'
завершается неудачно, поскольку байт 0xfc
не является допустимым 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_*
-category такие как LC_CTYPE
. Таким образом, самый надежный подход - установить LC_ALL
.
Однако (эффективно) установка LC_CTYPE
в C
обрабатывает строки так, как если бы каждый байт был своим собственным символом (не выполняется интерпретация на основе правил кодирования), без учета кодирования UTF-8 - multibyte-on-demand - которое использует OS X по умолчанию, где иностранные символы имеют многобайтовые кодировки.
В двух словах: установка LC_CTYPE
в C
заставляет оболочку и утилиты распознавать только основные английские буквы как буквы (те, которые находятся в 7-битном диапазоне ASCII), так что внешние символы. не будут рассматриваться как буквы, что приведет, например, к неудачным преобразованиям upper-/строчными буквами.
Опять же, это может быть хорошо, если вам не нужно сопоставлять многобайтовые символы, такие как é
, и просто хотите пропустить такие символы.
Если этого недостаточно и/или вы хотите понять причину исходной ошибки (включая определение того, какие входные байты вызвали проблему) и выполнить преобразования кодирования по требованию, читайте ниже.
Проблема в том, что кодировка входного файла не соответствует оболочке.
Более конкретно, входной файл содержит символы, закодированные таким образом, который недопустим в UTF-8 (как @Klas Lindbäck заявил в комментарии) - это то, что пытается сказать сообщение об ошибке sed
с помощью invalid byte sequence
.
Скорее всего, ваш входной файл использует однобайтовую 8-битную кодировку, такую как ISO-8859-1
, часто используемую для кодирования "западноевропейских" языков.
Пример:
Буква с акцентом à
имеет кодовую 0xE0
Unicode 0xE0
(224) - так же, как в ISO-8859-1
. Однако из-за характера кодирования UTF-8 эта единственная 0xC3 0xA0
представлена в виде 2 байтов - 0xC3 0xA0
, тогда как попытка передать один байт 0xE0
недопустима в UTF-8.
Здесь демонстрация проблемы с использованием строки voilà
закодированной как ISO-8859-1
, с à
представленной одним байтом (через строку bash в кавычках ANSI-C ($'...'
), которая использует \x{e0}
создать байт):
Обратите внимание на то, что команда sed
по сути является no-op, которая просто пропускает ввод, но она нам нужна, чтобы вызвать ошибку:
# -> '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 - потребуется более сложный подход для конкретной идентификации байтов invalid-in-UTF-8.)
Выполнение кодирования преобразований по требованию:
Стандартная утилита iconv
может использоваться для преобразования в (-t
) и/или из (-f
) кодировок; iconv -l
перечисляет все поддерживаемые.
Примеры:
Преобразование из ISO-8859-1
в действующую кодировку в оболочке (на основе LC_CTYPE
, по умолчанию UTF-8
-based), основываясь на приведенном выше примере:
# 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