Usando awk para remover a marca de ordem de bytes

105

Qual seria a aparência de um awkscript (presumivelmente de uma linha) para remover um BOM ?

Especificação:

  • imprimir todas as linhas após a primeira ( NR > 1)
  • para a primeira linha: se começar com #FE #FFou #FF #FE, remova-os e imprima o resto
Boldewyn
fonte

Respostas:

114

Experimente isto:

awk 'NR==1{sub(/^\xef\xbb\xbf/,"")}{print}' INFILE > OUTFILE

No primeiro registro (linha), remova os caracteres BOM. Imprima todos os registros.

Ou um pouco mais curto, usando o conhecimento de que a ação padrão no awk é imprimir o registro:

awk 'NR==1{sub(/^\xef\xbb\xbf/,"")}1' INFILE > OUTFILE

1 é a condição mais curta que sempre avalia como verdadeira, portanto, cada registro é impresso.

Aproveitar!

-- TERMO ADITIVO --

As perguntas frequentes sobre Unicode Byte Order Mark (BOM) incluem a seguinte tabela que lista os bytes de BOM exatos para cada codificação:

Bytes         |  Encoding Form
--------------------------------------
00 00 FE FF   |  UTF-32, big-endian
FF FE 00 00   |  UTF-32, little-endian
FE FF         |  UTF-16, big-endian
FF FE         |  UTF-16, little-endian
EF BB BF      |  UTF-8

Assim, você pode ver como \xef\xbb\xbfcorresponde aos EF BB BF UTF-8bytes de BOM da tabela acima.

Bartosz
fonte
1
Parece que o ponto no meio da instrução sub é demais (pelo menos, meu awk reclama disso). Ao lado disso é exatamente o que eu pesquisei, obrigado!
Boldewyn
5
Essa solução, no entanto, funciona apenas para arquivos codificados em UTF-8. Para outros, como UTF-16, consulte Wikipedia para a representação BOM correspondente: en.wikipedia.org/wiki/Byte_order_mark
Boldewyn
2
Então: awk '{if(NR==1)sub(/^\xef\xbb\xbf/,"");print}' INFILE > OUTFILEe certifique-se de que INFILE e OUTFILE são diferentes!
Steve Clay
1
Se você usou, perl -i.orig -pe 's/^\x{FFFE}//' badfilepode contar com suas variáveis ​​de ambiente PERL_UNICODE e / ou PERLIO para a codificação. PERL_UNICODE = SD funcionaria para UTF-8; para os outros, você precisaria do PERLIO.
tchrist
1
Talvez uma versão um pouco mais curta:awk 'NR==1{sub(/^\xef\xbb\xbf/,"")}1'
TrueY
122

Usando GNU sed(no Linux ou Cygwin):

# Removing BOM from all text files in current directory:
sed -i '1 s/^\xef\xbb\xbf//' *.txt

No FreeBSD:

sed -i .bak '1 s/^\xef\xbb\xbf//' *.txt

Vantagem de usar GNU ou FreeBSD sed: o -iparâmetro significa "no lugar" e atualizará os arquivos sem a necessidade de redirecionamentos ou truques estranhos.

No Mac:

Esta awksolução em outra resposta funciona , mas o sedcomando acima não funciona. Pelo menos no Mac (Sierra)sed não menciona o suporte de ala de escape hexadecimal \xef.

Um truque semelhante pode ser alcançado com qualquer programa direcionando para a spongeferramenta de moreutils :

awk '…' INFILE | sponge INFILE
Denilson Sá Maia
fonte
5
Tentei o segundo comando precisamente no Mac OS X e o resultado foi "sucesso", mas a substituição não ocorreu de fato.
Trejkaz
1
É importante notar que esses comandos substituem uma sequência de bytes específica, que é uma das marcas de ordem de bytes possíveis . Talvez seu arquivo tenha uma sequência diferente de BOM. (Não posso deixar de fazer isso, pois não tenho Mac)
Denilson Sá Maia
3
Quando tentei o segundo comando no OS X em um arquivo que usava 0xef 0xbb 0xbf como BOM, ele não fez a substituição.
John Wiseman
No OSX, só consegui fazer isso funcionar via perl, conforme mostrado aqui: stackoverflow.com/a/9101056/2063546
Ian
No OS X El Capitan 10.11.6, isso não funciona, mas a resposta oficial stackoverflow.com/a/1068700/9636 funciona bem.
Heath Borders
42

Não awk, mas mais simples:

tail -c +4 UTF8 > UTF8.nobom

Para verificar o BOM:

hd -n 3 UTF8

Se o BOM estiver presente, você verá: 00000000 ef bb bf ...

Steve Clay
fonte
6
Os BOMs têm 2 bytes para UTF-16 e 4 bytes para UTF-32 e, claro, não tem por que estar em UTF-8.
tchrist de
2
@KarolyHorvath Sim, precisamente. Seu uso não é recomendado. Isso quebra coisas. A codificação deve ser especificada por um protocolo de nível superior.
tchrist,
1
@tchrist: você quer dizer que quebra coisas quebradas? :) aplicativos adequados devem ser capazes de lidar com esse BOM.
Karoly Horvath
7
@KarolyHorvath, quero dizer, quebra muitos programas . Não foi isso que eu disse? Quando você abre um fluxo nas codificações UTF-16 ou UTF-32, o decodificador sabe que não deve contar o BOM. Quando você usa UTF-8, os decodificadores apresentam o BOM como dados. Este é um erro de sintaxe em inúmeros programas. Até mesmo o decodificador de Java se comporta dessa maneira, POR DESIGN! BOMs em arquivos UTF-8 estão perdidos e um pé no saco: eles são um erro! Eles quebram muitas coisas. Mesmo só cat file1.utf8 file2.utf8 file3.utf3 > allfiles.utf8vai ser quebrado. Nunca use um BOM em UTF-8. Período.
tchrist,
6
hdnão está disponível no Mac OS X (a partir de 10.8.2), de modo a verificar a existência de um UTF-8 BOM lá você pode usar o seguinte: head -c 3 file | od -t x1.
mklement0
21

Além de converter terminações de linha CRLF em LF, dos2unixtambém remove BOMs:

dos2unix *.txt

dos2unix também converte arquivos UTF-16 com BOM (mas não arquivos UTF-16 sem BOM) em UTF-8 sem BOM:

$ printf '\ufeffä\n'|iconv -f utf-8 -t utf-16be>bom-utf16be
$ printf '\ufeffä\n'|iconv -f utf-8 -t utf-16le>bom-utf16le
$ printf '\ufeffä\n'>bom-utf8
$ printf 'ä\n'|iconv -f utf-8 -t utf-16be>utf16be
$ printf 'ä\n'|iconv -f utf-8 -t utf-16le>utf16le
$ printf 'ä\n'>utf8
$ for f in *;do printf '%11s %s\n' $f $(xxd -p $f);done
bom-utf16be feff00e4000a
bom-utf16le fffee4000a00
   bom-utf8 efbbbfc3a40a
    utf16be 00e4000a
    utf16le e4000a00
       utf8 c3a40a
$ dos2unix -q *
$ for f in *;do printf '%11s %s\n' $f $(xxd -p $f);done
bom-utf16be c3a40a
bom-utf16le c3a40a
   bom-utf8 c3a40a
    utf16be 00e4000a
    utf16le e4000a00
       utf8 c3a40a
Lri
fonte
3

Sei que a pergunta foi direcionada ao unix / linux, achei que valeria a pena citar uma boa opção para quem tem desafios para unix (em windows, com UI).
Eu encontrei o mesmo problema em um projeto WordPress (o BOM estava causando problemas com feed de rss e validação de página) e tive que examinar todos os arquivos em uma árvore de diretório bem grande para encontrar aquele que estava com o BOM. Encontrou um aplicativo chamado Replace Pioneer e nele:

Batch Runner -> Pesquisar (para encontrar todos os arquivos nas subpastas) -> Substituir modelo -> BOM remover BOM (há um modelo pronto de pesquisa e substituição para isso).

Não era a solução mais elegante e exigia a instalação de um programa, o que é uma desvantagem. Mas depois que descobri o que estava acontecendo ao meu redor, funcionou perfeitamente (e encontrei 3 arquivos entre cerca de 2300 que estavam com o BOM).

Arnon Zamir
fonte
1
Fiquei muito feliz quando encontrei sua solução, porém não tenho o privilégio de instalar o software no computador da empresa. Levei muito tempo hoje, até que eu descobrisse a alternativa: Usar o Notepad ++ com o plugin PythonScript. superuser.com/questions/418515/… Obrigado de qualquer maneira!
Hoàng Long