Estou tentando substituir uma string em um Makefile no Mac OS X para compilação cruzada no iOS. A cadeia incorporou aspas duplas. 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
Tentei escapar das 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 um bom tempo depurando o problema. Alguém sabe como sed
imprimir a posição da sequência de bytes ilegal? Ou alguém sabe qual é a sequência ilegal de bytes?
LC_CTYPE=C && LANG=C && sed command
LANG
coisa. Suspiro ....sed
(como também usado no OS X) requer-i ''
(argumento de opção de cadeia vazia e separado) para atualização no local sem um arquivo de backup; com GNUsed
, Só-i
por si só funciona - ver stackoverflow.com/a/40777793/45375Respostas:
Um comando de amostra que exibe o sintoma:
sed 's/./@/' <<<$'\xfc'
falha, porque o byte0xfc
não é um caractere UTF-8 válido.Observe que, por outro lado, o GNU
sed
(Linux, mas também instalável no macOS) simplesmente passa o byte inválido, sem relatar um erro.Usar a resposta anteriormente aceita é uma opção se você não se importa em perder o suporte para o seu local verdadeiro (se você estiver em um sistema nos EUA e nunca precisar lidar com caracteres estrangeiros, isso pode ser bom).
No entanto, o mesmo efeito pode ser tido ad-hoc para um único comando única :
Nota: O que importa é uma configuração efetiva
LC_CTYPE
deC
, portantoLC_CTYPE=C sed ...
, normalmente também funcionaria, mas, seLC_ALL
for definido (para algo diferente deC
), ele substituirá asLC_*
variáveis de categoria individual , comoLC_CTYPE
. Assim, a abordagem mais robusta é definirLC_ALL
.No entanto, a configuração (efetivamente)
LC_CTYPE
deC
tratar sequências de caracteres como se cada byte fosse seu próprio caractere ( nenhuma interpretação baseada em regras de codificação é executada), sem levar em consideração a codificação - multibyte-on-demand - UTF-8 que o OS X emprega por padrão , onde caracteres estrangeiros têm codificações multibyte .Em poucas palavras: a configuração
LC_CTYPE
paraC
faz com que o shell e os utilitários reconheçam apenas letras em inglês básicas como letras (aquelas no intervalo ASCII de 7 bits), de modo que caracteres estrangeiros. não serão tratados como letras , causando, por exemplo, conversões em maiúsculas / minúsculas.Novamente, isso pode ser bom se você não precisar corresponder a caracteres codificados com vários bytes, como por exemplo
é
, e simplesmente desejar passar esses caracteres .Se isso for insuficiente e / ou você quiser entender a causa do erro original (incluindo a determinação de 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 maneira que não é válida em UTF-8 (como @Klas Lindbäck afirmou em um comentário) - é o que a
sed
mensagem de erro está tentando dizerinvalid byte sequence
.Muito provavelmente, seu arquivo de entrada usa uma codificação de 8 bits de byte único , como
ISO-8859-1
frequentemente usada para codificar idiomas da "Europa Ocidental".Exemplo:
A letra acentuada
à
possui o ponto de código Unicode0xE0
(224) - o mesmo que emISO-8859-1
. No entanto, devido à natureza da codificação UTF-8 , esse único ponto de código é representado como 2 bytes -0xC3 0xA0
, enquanto a tentativa de passar o byte único0xE0
é inválida em UTF-8.Aqui está uma demonstração do problema usando a string
voilà
codificada comoISO-8859-1
, com aà
representada como um byte (por meio de uma string bash com citação ANSI-C ($'...'
) usada\x{e0}
para criar o byte):Observe que o
sed
comando é efetivamente um no-op que simplesmente passa a entrada, mas precisamos provocar o erro:Para simplesmente ignorar o problema , a
LCTYPE=C
abordagem acima pode ser usada:Se você deseja determinar quais partes da entrada causam o problema , tente o seguinte:
A saída mostrará todos os bytes com o bit alto definido (bytes que excedem o intervalo ASCII de 7 bits) na forma hexadecimal. (Observe, no entanto, que isso também inclui sequências multibyte UTF-8 codificadas corretamente - uma abordagem mais sofisticada seria necessária para identificar especificamente bytes inválidos em UTF-8.)
Executando conversões de codificação sob demanda :
O utilitário padrão
iconv
pode ser usado para converter para codificações (-t
) e / ou de (-f
);iconv -l
lista todos os suportados.Exemplos:
Converta FROM
ISO-8859-1
na codificação em vigor no shell (com base emLC_CTYPE
, que éUTF-8
baseada em padrão), com base no exemplo acima:Observe que essa conversão permite corresponder corretamente caracteres estrangeiros :
Para converter a entrada BACK para
ISO-8859-1
após o processamento, basta canalizar o resultado para outroiconv
comando:fonte
LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}'
imprimesed: RE error: illegal byte sequence
para mim na Sierra.echo $LC_ALL
geraen_US.UTF-8
FWIW.LC_ALL
substitui todas as outrasLC_*
variáveis, incluindoLC_CTYPE
, conforme explicado na resposta.Adicione as seguintes linhas ao seu
~/.bash_profile
ou~/.zshrc
arquivo (s).fonte
LC_CTYPE
comoC
faz com que cada byte nas strings tenha seu próprio caractere sem aplicar nenhuma regra de codificação. Como uma violação das regras de codificação (UTF-8) causou o problema original, isso faz com que o problema desapareça. No entanto, o preço pago é que o shell e os utilitários somente reconheçam as letras básicas em inglês (as da faixa ASCII de 7 bits) como letras. Veja minha resposta para mais.LC_CTYPE=C sed …
, ou seja, apenas no comando sed.Minha solução alternativa estava usando o Perl:
fonte
A resposta do mklement0 é ótima, mas tenho alguns pequenos ajustes.
Parece uma boa idéia especificar explicitamente
bash
a codificação ao usariconv
. Além disso, devemos acrescentar uma marca de ordem de bytes ( mesmo que o padrão unicode não a recomende ), pois pode haver confusões legítimas entre UTF-8 e ASCII sem uma marca de ordem de bytes . Infelizmente,iconv
não precede uma marca de ordem de bytes quando você especifica explicitamente um endianness (UTF-16BE
ouUTF-16LE
), portanto, precisamos usar oUTF-16
que usa endianness específico da plataforma e depoisfile --mime-encoding
descobrir a verdadeira endiannessiconv
usada.(Eu coloco todas as minhas codificações em maiúsculas, porque quando você lista todas
iconv
as codificações suportadas comiconv -l
todas elas estão em maiúsculas.)fonte
file -b --mime-encoding
para descobrir e reportar a codificação de um arquivo. Há alguns aspectos que vale a pena abordar, no entanto, que farei em comentários separados.LC_CTYPE
valor padrão é geralmente<lang_region>.UTF-8
, então qualquer arquivo sem uma BOM (marca de ordem de bytes) é, portanto, interpretado como um arquivo UTF-8. É apenas no mundo Windows que a pseudo-BOM0xef 0xbb 0xff
é usada; por definição, o UTF-8 não precisa de uma lista técnica e não é recomendado (como você declara); fora do mundo do Windows, essa pseudo-lista técnica faz com que as coisas quebrem .Unfortunately, iconv doesn't prepend a byte-order mark when you explicitly specify an endianness (UTF-16BE or UTF-16LE)
: isso é por design: se você especificar o endianness explicitamente , não haverá necessidade de refleti-lo também por meio de uma lista técnica, portanto, nenhum será adicionado.LC_*
/LANG
variáveis:bash
,ksh
, ezsh
(possivelmente outros, mas nãodash
) fazer respeitar a codificação de caracteres; verifique em shells do tipo POSIX com um código de idioma baseado em UTF-8 comv='ä'; echo "${#v}"
: um shell compatível com UTF-8 deve relatar1
; ou seja, ele deve reconhecer a sequência de bytes múltiplosä
(0xc3 0xa4
), como um único caractere. Talvez ainda mais importante, porém: os utilitários padrão (sed
,awk
,cut
, ...) também precisam ser locale / codificação-aware, e enquanto a maioria deles na moderna Unix-like plataformas são, há exceções, comoawk
no OSX, ecut
no Linux.file
reconhecer a pseudo-BOM UTF-8, mas o problema é que a maioria dos utilitários Unix que processam arquivos não o fazem , e geralmente quebram ou pelo menos se comportam mal quando confrontados com um. Sem uma BOM,file
identifica corretamente um arquivo de bytes de 7 bits como ASCII e um que possui caracteres de vários bytes UTF-8 válidos como UTF-8. A beleza do UTF-8 é que ele é um superconjunto do ASCII: qualquer arquivo ASCII válido é, por definição, um arquivo UTF-8 válido (mas não vice-versa); é perfeitamente para seguro para tratar um arquivo ASCII como UTF-8 (o que tecnicamente é, ele só acontece de não contêm caracteres multi-byte.)Você simplesmente precisa canalizar um comando iconv antes do comando sed . Ex com entrada file.txt:
A opção -f é o conjunto de códigos 'from' e a opção -t é a conversão do conjunto de códigos 'to'.
Cuidado, as páginas da Web geralmente mostram letras minúsculas assim <charset = iso-8859-1 "/> e iconv usa letras maiúsculas. Você tem uma lista de conjuntos de códigos suportados por iconv em seu sistema com o comando iconv -l
UTF8-MAC é um conjunto de códigos OS Mac moderno para conversão.
fonte
Eu fiz parte do caminho para responder às perguntas acima apenas usando tr .
Eu tenho um arquivo .csv que é um extrato do cartão de crédito e estou tentando importá-lo para o Gnucash. Como estou na Suíça, tenho que lidar com palavras como Zurique. Suspeitando que o Gnucash não goste "" nos campos numéricos, decido simplesmente substituir todos
com
Aqui vai:
Eu usei od para lançar alguma luz: Observe o 374 no meio dessa saída od -c
Então pensei em tentar convencer tr a substituir 374 pelo código de bytes correto. Então, primeiro, tentei algo simples, que não funcionou, mas teve o efeito colateral de me mostrar onde estava o byte problemático:
Você pode ver tr fianças no caractere 374.
O uso de perl parece evitar esse problema
fonte
Minha solução alternativa estava usando o gnu
sed
. Funcionou bem para os meus propósitos.fonte
sed
é uma opção se você deseja ignorar bytes inválidos no fluxo de entrada (não há necessidade deLC_ALL=C sed ...
solução alternativa), porque o GNUsed
simplesmente passa bytes inválidos ao invés de relatar um erro, mas observe que se você deseja reconhecer e processar adequadamente todos caracteres na sequência de entrada, não há como alterar a codificação da entrada primeiro (normalmente, comiconv
).