Erro RE: sequência de bytes ilegal no Mac OS X

Eu estou tentando replace uma seqüência de caracteres em um Makefile no Mac OS X para cross-compiling para iOS. A string 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 escaping 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?

Um exemplo de comando 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 replaceá variables ​​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 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 os 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 com 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 input causaram o problema) e executar conversões de codificação sob demanda, leia abaixo.


O problema é que a codificação do arquivo de input não coincide com a do shell.
Mais especificamente, o arquivo de input 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 .

Muito provavelmente, seu arquivo de input 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 o à representado 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 input, 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 input 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 sequê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 input 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 

Adicione as seguintes linhas ao seu arquivo ~/.bash_profile ou ~/.zshrc .

 export LC_CTYPE=C export LANG=C 

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 ), portanto, precisamos usar o UTF-16 , que usa o endianness específico da plataforma e 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 

Minha solução foi usando Perl:

 find . -type f -print0 | xargs -0 perl -pi -e 's/was/now/g' 

Minha solução estava usando o gnu sed . Funcionou bem para os meus propósitos.