regex - Erro RE: sequência de bytes ilegal no Mac OS X
macos bash (4)
Eu estou tentando substituir uma seqüência de caracteres em um Makefile no Mac OS X para cross-compiling para iOS. A cadeia tem aspas duplas incorporadas. O comando é:
sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure
E o erro é:
sed: RE error: illegal byte sequence
Eu tentei escapar aspas duplas, vírgulas, traços e dois pontos sem alegria. Por exemplo:
sed -i "" 's|\"iphoneos-cross\"\,\"llvm-gcc\:\-O3|\"iphoneos-cross\"\,\"clang\:\-Os|g' Configure
Estou tendo muita dificuldade para depurar o problema. Alguém sabe como obter sed
para imprimir a posição da seqüência de bytes ilegais? Ou alguém sabe o que é a sequência ilegal de bytes?
Adicione as seguintes linhas ao seu arquivo ~/.bash_profile
ou ~/.zshrc
.
export LC_CTYPE=C
export LANG=C
Minha solução estava usando o gnu sed
. Funcionou bem para os meus propósitos.
Um comando de exemplo que exibe o sintoma: sed 's/./@/' <<<$'\xfc'
falha, porque o byte 0xfc
não é um caractere UTF-8 válido.
Note que, ao contrário, o GNU sed
(Linux, mas também instalável no macOS) simplesmente passa o byte inválido, sem reportar um erro.
Usar a resposta anteriormente aceita é uma opção se você não se importar em perder suporte para sua verdadeira localidade (se você estiver em um sistema dos EUA e nunca precisar lidar com caracteres estrangeiros, isso pode ser bom).
No entanto, o mesmo efeito pode ser ad-hoc para um único comando :
LC_ALL=C sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure
Nota: O que importa é uma configuração LC_CTYPE
efetiva de C
, portanto LC_CTYPE=C sed ...
normalmente também funcionará, mas se LC_ALL
estiver configurada (para algo diferente de C
), ela substituirá variáveis de categoria LC_*
individuais, como como LC_CTYPE
. Assim, a abordagem mais robusta é definir LC_ALL
.
No entanto, (efetivamente) configurar LC_CTYPE
para C
trata cadeias de caracteres como se cada byte fosse seu próprio caractere ( nenhuma interpretação baseada em regras de codificação é executada), sem considerar a codificação - multibyte-on-demand - UTF-8 que o OS X emprega por padrão, onde os caracteres estrangeiros possuem codificações multibyte .
Resumindo: a configuração de LC_CTYPE
para C
faz com que o shell e os utilitários reconheçam somente letras inglesas básicas como letras (aquelas no intervalo ASCII de 7 bits), de modo que caracteres estrangeiros. não serão tratados como letras , fazendo com que, por exemplo, as conversões de maiúsculas / minúsculas falhem.
Novamente, isso pode ser bom se você não precisar corresponder caracteres codificados por multibytes, como é
, e simplesmente quiser passar esses caracteres .
Se isso for insuficiente e / ou você quiser entender a causa do erro original (incluindo determinar quais bytes de entrada causaram o problema) e executar conversões de codificação sob demanda, leia abaixo.
O problema é que a codificação do arquivo de entrada não corresponde à do shell.
Mais especificamente, o arquivo de entrada contém caracteres codificados de uma forma que não é válida em UTF-8 (como @Klas Lindbäck declarou em um comentário) - é o que a mensagem de erro sed
está tentando dizer por invalid byte sequence
.
Provavelmente, o seu arquivo de entrada usa uma codificação de 8 bits de um único byte , como ISO-8859-1
, freqüentemente usada para codificar idiomas "da Europa Ocidental".
Exemplo:
A letra acentuada à
tem um ponto de código Unicode 0xE0
(224) - o mesmo que no ISO-8859-1
. No entanto, devido à natureza da codificação UTF-8 , esse único ponto de código é representado como 2 bytes - 0xC3 0xA0
, enquanto que tentar passar o único byte 0xE0
é inválido em UTF-8.
Aqui está uma demonstração do problema usando a string voilà
codificada como ISO-8859-1
, com a à
representada como um byte (via uma string bash ANSI-C ( $'...'
) que usa \x{e0}
para criar o byte):
Note que o comando sed
é efetivamente um no-op que simplesmente passa a entrada, mas nós precisamos que ele provoque o erro:
# -> 'illegal byte sequence': byte 0xE0 is not a valid char.
sed 's/.*/&/' <<<$'voil\x{e0}'
Para simplesmente ignorar o problema , a abordagem LCTYPE=C
acima pode ser usada:
# No error, bytes are passed through ('á' will render as '?', though).
LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}'
Se você quiser determinar quais partes da entrada causam o problema , tente o seguinte:
# 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}'
A saída mostrará todos os bytes que possuem o conjunto de bits alto (bytes que excedem o intervalo ASCII de 7 bits) em formato hexadecimal. (Observe, no entanto, que isso também inclui seqüências de multibyte UTF-8 codificadas corretamente - uma abordagem mais sofisticada seria necessária para identificar especificamente bytes inválidos em UTF-8.)
Realizando conversões de codificação sob demanda :
O utilitário padrão iconv
pode ser usado para converter em ( -t
) e / ou de ( -f
) codificações; iconv -l
lista todos os suportados.
Exemplos:
Converte FROM ISO-8859-1
para a codificação em vigor no shell (com base em LC_CTYPE
, que é baseado em UTF-8
por padrão), com base no exemplo acima:
# Converts to UTF-8; output renders correctly as 'voilà'
sed 's/.*/&/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')"
Observe que essa conversão permite que você corresponda corretamente a caracteres estrangeiros :
# Correctly matches 'à' and replaces it with 'ü': -> 'voilü'
sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')"
Para converter a entrada BACK para ISO-8859-1
após o processamento, basta enviar o resultado para outro comando iconv
:
sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')" | iconv -t ISO-8859-1
A resposta do mklement0 é ótima, mas eu tenho alguns pequenos ajustes.
Parece uma boa idéia especificar explicitamente a codificação do bash
ao usar iconv
. Além disso, devemos prefixar uma marca de ordem de byte ( mesmo que o padrão unicode não o recomende ) porque pode haver confusões legítimas entre UTF-8 e ASCII sem uma marca de ordem de byte . Infelizmente, o iconv
não prefixa uma marca de ordem de byte quando você especifica explicitamente um endianness ( UTF-16BE
ou UTF-16LE
), então precisamos usar o UTF-16
, que usa o endianness específico da plataforma, e então usar o file --mime-encoding
para descobrir o verdadeiro endianness iconv
usado.
(Eu maiúscula todas as minhas codificações porque quando você lista todas as codificações suportadas pelo iconv -l
com iconv -l
elas são todas maiúsculas.)
# 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