Uso de awk para eliminar la marca de orden de bytes

105

¿Cómo se vería un awkguión (presumiblemente una sola línea) para eliminar una lista de materiales ?

Especificación:

  • imprimir cada línea después de la primera ( NR > 1)
  • para la primera línea: si comienza con #FE #FFo #FF #FE, elimínelos e imprima el resto
Boldewyn
fuente

Respuestas:

114

Prueba esto:

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

En el primer registro (línea), elimine los caracteres BOM. Imprime todos los registros.

O un poco más corto, sabiendo que la acción predeterminada en awk es imprimir el registro:

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

1 es la condición más corta que siempre se evalúa como verdadera, por lo que se imprime cada registro.

¡Disfrutar!

- ADENDA -

Las preguntas frecuentes sobre la marca de orden de bytes (BOM) Unicode incluyen la siguiente tabla que enumera los bytes de BOM exactos para cada codificación:

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

Por lo tanto, puede ver cómo \xef\xbb\xbfcorresponde a los EF BB BF UTF-8bytes de la lista de materiales en la tabla anterior.

Bartosz
fuente
1
Parece que el punto en el medio de la declaración secundaria es demasiado (al menos, mi awk se queja de ello). Además de esto, es exactamente lo que busqué, ¡gracias!
Boldewyn
5
Sin embargo, esta solución solo funciona para archivos codificados en UTF-8. Para otros, como UTF-16, consulte Wikipedia para la representación BOM correspondiente: en.wikipedia.org/wiki/Byte_order_mark
Boldewyn
2
Entonces: ¡ awk '{if(NR==1)sub(/^\xef\xbb\xbf/,"");print}' INFILE > OUTFILEy asegúrese de que INFILE y OUTFILE sean diferentes!
Steve Clay
1
Si lo utilizó perl -i.orig -pe 's/^\x{FFFE}//' badfile, podría confiar en sus envariables PERL_UNICODE y / o PERLIO para la codificación. PERL_UNICODE = SD funcionaría para UTF-8; para los demás, necesitarías PERLIO.
tchrist
1
Quizás una versión un poco más corta:awk 'NR==1{sub(/^\xef\xbb\xbf/,"")}1'
TrueY
122

Usando GNU sed(en Linux o Cygwin):

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

En FreeBSD:

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

Ventaja de usar GNU o FreeBSD sed: el -iparámetro significa "en el lugar" y actualizará los archivos sin necesidad de redirecciones o trucos extraños.

En Mac:

Esta awksolución en otra respuesta funciona , pero el sedcomando anterior no funciona. Al menos en Mac (Sierra), la seddocumentación no menciona el soporte ala de escape hexadecimal \xef.

Se puede lograr un truco similar con cualquier programa conectando a la spongeherramienta desde moreutils :

awk '…' INFILE | sponge INFILE
Denilson Sá Maia
fuente
5
Probé el segundo comando precisamente en Mac OS X y el resultado fue "éxito", pero la sustitución no se produjo en realidad.
Trejkaz
1
Vale la pena señalar que estos comandos reemplazan una secuencia de bytes específica, que es una de las posibles marcas de orden de bytes . Quizás su archivo tuviera una secuencia de lista de materiales diferente. (No puedo ayudar más que eso, ya que no tengo una Mac)
Denilson Sá Maia
3
Cuando probé el segundo comando en OS X en un archivo que usaba 0xef 0xbb 0xbf como lista de materiales, en realidad no hizo la sustitución.
John Wiseman
En OSX, solo pude hacer que esto funcione a través de perl, como se muestra aquí: stackoverflow.com/a/9101056/2063546
Ian
En OS X El Capitan 10.11.6, esto no funciona, pero la respuesta oficial stackoverflow.com/a/1068700/9636 funciona bien.
Heath Borders
42

No awk, pero más simple:

tail -c +4 UTF8 > UTF8.nobom

Para comprobar la lista de materiales:

hd -n 3 UTF8

Si BOM está presente, verá: 00000000 ef bb bf ...

Steve Clay
fuente
6
Las listas de materiales son 2 bytes para UTF-16 y 4 bytes para UTF-32 y, por supuesto, no tienen por qué estar en UTF-8 en primer lugar.
tchrist
2
@KarolyHorvath Sí, precisamente. No se recomienda su uso. Rompe cosas. La codificación debe especificarse mediante un protocolo de nivel superior.
tchrist
1
@tchrist: ¿quieres decir que rompe cosas rotas? :) las aplicaciones adecuadas deberían poder manejar esa lista de materiales.
Karoly Horvath
7
@KarolyHorvath Me refiero a que rompe muchos programas . ¿No es eso lo que dije? Cuando abre un flujo en codificaciones UTF-16 o UTF-32, el decodificador sabe que no debe contar la lista de materiales. Cuando usa UTF-8, los decodificadores presentan la lista de materiales como datos. Este es un error de sintaxis en innumerables programas. Incluso el decodificador de Java se comporta de esta manera, ¡POR DISEÑO! Las listas de materiales en archivos UTF-8 están fuera de lugar y son un dolor en el trasero: ¡ son un error! Rompen muchas cosas. Incluso solo cat file1.utf8 file2.utf8 file3.utf3 > allfiles.utf8se romperá. Nunca use una lista de materiales en UTF-8. Período.
tchrist
6
hdNo está disponible en OS X (a partir de 10.8.2), por lo que para comprobar si hay una BOM UTF-8 no se puede utilizar el siguiente: head -c 3 file | od -t x1.
mklement0
21

Además de convertir los finales de línea CRLF a LF, dos2unixtambién elimina las listas de materiales:

dos2unix *.txt

dos2unix también convierte archivos UTF-16 con una BOM (pero no archivos UTF-16 sin una BOM) a UTF-8 sin una 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
fuente
3

Sé que la pregunta estaba dirigida a Unix / Linux, pensé que valdría la pena mencionar una buena opción para los que tienen problemas con Unix (en Windows, con una interfaz de usuario).
Me encontré con el mismo problema en un proyecto de WordPress (BOM estaba causando problemas con el feed de rss y la validación de la página) y tuve que buscar en todos los archivos en un árbol de directorios bastante grande para encontrar el que estaba con BOM. Encontré una aplicación llamada Reemplazar Pioneer y en ella:

Batch Runner -> Buscar (para encontrar todos los archivos en las subcarpetas) -> Reemplazar plantilla -> Binario eliminar lista de materiales (hay una plantilla de búsqueda y reemplazo lista para esto).

No era la solución más elegante y requería instalar un programa, lo cual es una desventaja. Pero una vez que descubrí lo que estaba pasando a mi alrededor, funcionó a las mil maravillas (y encontré 3 archivos de aproximadamente 2300 que tenían BOM).

Arnon Zamir
fuente
1
Estoy muy feliz cuando encontré su solución, sin embargo, no tengo el privilegio de instalar software en la computadora de la empresa. Hoy me tomó mucho tiempo, hasta que descubrí la alternativa: usar Notepad ++ con el complemento PythonScript. superuser.com/questions/418515/… ¡ Gracias de todos modos!
Hoàng Long