Ошибка RE: незаконная последовательность байтов на Mac OS X
Я пытаюсь заменить строку в Makefile на Mac OS X для кросс-компиляции в iOS. Строка имеет встроенные двойные кавычки. Команда:
sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure
и ошибка:
sed: RE error: illegal byte sequence
Я пытался избежать двойных кавычек, запятых, тире, двоеточий и без радости. Например:
sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure
Я чертовски время отладки проблемы. Кто-нибудь знает, как получить sed
для печати позиции незаконной последовательности байтов? Или кто-нибудь знаете, что такое незаконная последовательность байтов?
5 ответов
пример команды, которая показывает симптом:sed 's/./@/' <<<$'\xfc'
не удается, потому что byte 0xfc
не является допустимым символом UTF-8.
Обратите внимание, что, напротив, GNU sed
(Linux, но также устанавливаемый на macOS) просто передает недопустимый байт, не сообщая об ошибке.
С помощью ранее принятый ответ это вариант, если вы не против потерять поддержку вашего истинного locale (если вы находитесь в системе США и вам никогда не нужно иметь дело с иностранными персонажами, это может быть хорошо.)
на такой же эффект может быть 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
to C
лечит строки как если бы каждый байт был его собственным символом (нет выполняется интерпретация на основе правил кодирования), с нет связи для-multibyte-on-demand -кодировка UTF-8 что OS X использует по умолчанию, где символы есть многобайтовых кодировок.
в двух словах: задание LC_CTYPE
to C
заставляет оболочку и утилиты распознавать только основные английские буквы как буквы (те, которые находятся в 7-битном диапазоне ASCII), так что иностранные символы. не будет рассматриваться как буквы, вызывая, например, сбой преобразования верхнего/нижнего регистра.
опять же, это может быть хорошо, если вам не нужно матч многобайтовые символы, такие как é
и просто хочу передать такие символы через.
если этого недостаточно и/или вы хотите понять причину исходной ошибки (включая определение того, какие входные байты вызвали проблему) и выполнить конвертирование кодировки по требованию Читать далее ниже.
проблема в том, что кодировка входного файла не соответствует оболочке.
Точнее, входной файл содержит символы, закодированные таким образом, что это недопустимо в UTF-8 (как заявил @Klas Lindbäck в комментарии) - это то, что sed
сообщение об ошибке пытается сказать invalid byte sequence
.
скорее всего, ваш входной файл использует однобайтовая 8-битная кодировка например ISO-8859-1
, часто используется для кодирования "западноевропейских" языков.
пример:
буква С ударением à
имеет Unicode codepoint 0xE0
(224) - то же, что и в ISO-8859-1
. Однако, из-за природы UTF-8 кодировка, эта единственная кодовая точка представлена как 2 байт 0xC3 0xA0
, в то время как пытается передать один байт 0xE0
is недействительным под UTF-8.
здесь демонстрация проблема используя строку voilà
кодируется как ISO-8859-1
С à
представлены один байт (через ANSI-C-цитируемая строка bash ($'...'
), которая использует \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 - более сложный подход, необходимый для конкретного определения недопустимых байтов в 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}')"
для преобразования входных данных обратно в ISO-8859-1
после обработки просто передайте результат другому :
sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')" | iconv -t ISO-8859-1
mklement0 это отлично, но у меня есть несколько небольших настроек.
кажется хорошей идеей явно указать bash
кодировка при использовании iconv
. Кроме того, мы должны добавить Знак порядка байтов (даже если стандарт unicode не рекомендует его), поскольку могут быть законные путаницы между UTF-8 и ASCII без знака байтового порядка. К сожалению, iconv
не добавлять метку порядка байтов, когда вы явно укажите endianness (UTF-16BE
или UTF-16LE
), поэтому нам нужно использовать UTF-16
, который использует специфичную для платформы endianness, а затем использует file --mime-encoding
чтобы открыть истинную endianness iconv
используется.
(Я в верхнем регистре все мои кодировки, потому что когда вы перечисляете все iconv
поддерживаемые кодировки с iconv -l
они все заглавные.)
# Find out MY_FILE's encoding
# We'll convert back to this at the end
FILE_ENCODING="$( file --brief --mime-encoding MY_FILE )"
# Find out bash's encoding, with which we should encode
# MY_FILE so sed doesn't fail with
# sed: RE error: illegal byte sequence
BASH_ENCODING="$( locale charmap | tr [:lower:] [:upper:] )"
# Convert to UTF-16 (unknown endianness) so iconv ensures
# we have a byte-order mark
iconv -f "$FILE_ENCODING" -t UTF-16 MY_FILE > MY_FILE.utf16_encoding
# Whether we're using UTF-16BE or UTF-16LE
UTF16_ENCODING="$( file --brief --mime-encoding MY_FILE.utf16_encoding )"
# Now we can use MY_FILE.bash_encoding with sed
iconv -f "$UTF16_ENCODING" -t "$BASH_ENCODING" MY_FILE.utf16_encoding > MY_FILE.bash_encoding
# sed!
sed 's/.*/&/' MY_FILE.bash_encoding > MY_FILE_SEDDED.bash_encoding
# now convert MY_FILE_SEDDED.bash_encoding back to its original encoding
iconv -f "$BASH_ENCODING" -t "$FILE_ENCODING" MY_FILE_SEDDED.bash_encoding > MY_FILE_SEDDED
# Now MY_FILE_SEDDED has been processed by sed, and is in the same encoding as MY_FILE