Użycie awk do usunięcia znaku kolejności bajtów

105

Jak wyglądałby awkskrypt (prawdopodobnie jednolinijkowy) służący do usuwania zestawienia komponentów ?

Specyfikacja:

  • wypisz każdy wiersz po pierwszym ( NR > 1)
  • dla pierwszego wiersza: jeśli zaczyna się od #FE #FFlub #FF #FE, usuń je i wydrukuj resztę
Boldewyn
źródło

Odpowiedzi:

114

Spróbuj tego:

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

W pierwszym rekordzie (wierszu) usuń znaki zestawienia komponentów. Wydrukuj każdy rekord.

Lub trochę krócej, wiedząc, że domyślną akcją w awk jest wydrukowanie rekordu:

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

1 to najkrótszy warunek, który zawsze jest prawdziwy, więc każdy rekord jest drukowany.

Cieszyć się!

- DODATEK -

Często zadawane pytania dotyczące znaku kolejności bajtów Unicode (BOM) zawiera poniższą tabelę zawierającą dokładne bajty BOM dla każdego kodowania:

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

W ten sposób możesz zobaczyć, jak \xef\xbb\xbfodpowiada EF BB BF UTF-8bajtom BOM z powyższej tabeli.

Bartosz
źródło
1
Wygląda na to, że kropka w środku zdania podrzędnego jest za duża (przynajmniej mój awk narzeka na to). Poza tym to jest dokładnie to, czego szukałem, dzięki!
Boldewyn
5
To rozwiązanie działa jednak tylko w przypadku plików zakodowanych w UTF-8. Dla innych, takich jak UTF-16, zapoznaj się z odpowiednią reprezentacją BOM w Wikipedii: en.wikipedia.org/wiki/Byte_order_mark
Boldewyn
2
A więc: awk '{if(NR==1)sub(/^\xef\xbb\xbf/,"");print}' INFILE > OUTFILEi upewnij się, że INFILE i OUTFILE są różne!
Steve Clay
1
Jeśli perl -i.orig -pe 's/^\x{FFFE}//' badfileużywałeś, możesz polegać na swoich zmiennych środowiskowych PERL_UNICODE i / lub PERLIO do kodowania. PERL_UNICODE = SD będzie działać dla UTF-8; dla innych potrzebujesz PERLIO.
tchrist
1
Może trochę krótsza wersja:awk 'NR==1{sub(/^\xef\xbb\xbf/,"")}1'
TrueY
122

Korzystanie z GNU sed(w systemie Linux lub Cygwin):

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

W FreeBSD:

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

Zaleta korzystania z GNU lub FreeBSD sed: -iparametr oznacza „na miejscu” i będzie aktualizował pliki bez potrzeby przekierowań lub dziwnych sztuczek.

Na komputerze Mac:

To awkrozwiązanie w innej odpowiedzi działa , ale sedpowyższe polecenie nie działa. Przynajmniej na Macu (Sierra) seddokumentacja nie wspomina o obsłudze znaków szesnastkowych ucieczki ala \xef.

Podobną sztuczkę można osiągnąć w dowolnym programie, podłączając do spongenarzędzia z moreutils :

awk '…' INFILE | sponge INFILE
Denilson Sá Maia
źródło
5
Wypróbowałem drugie polecenie dokładnie w systemie Mac OS X i zakończyło się to „sukcesem”, ale zamiana nie nastąpiła.
Trejkaz
1
Warto zauważyć, że te polecenia zastępują jedną określoną sekwencję bajtów, która jest jednym z możliwych znaczników kolejności bajtów . Być może twój plik miał inną sekwencję BOM. (Nie mogę nic na to poradzić, ponieważ nie mam Maca)
Denilson Sá Maia,
3
Kiedy próbowałem drugiego polecenia w systemie OS X na pliku, który używał 0xef 0xbb 0xbf jako BOM, w rzeczywistości nie wykonało podstawienia.
John Wiseman
W OSX mogłem to uruchomić tylko przez perl, jak pokazano tutaj: stackoverflow.com/a/9101056/2063546
Ian
W systemie OS X El Capitan 10.11.6to nie działa, ale oficjalna odpowiedź stackoverflow.com/a/1068700/9636 działa dobrze.
Heath Borders
42

Nie awk, ale prostsze:

tail -c +4 UTF8 > UTF8.nobom

Aby sprawdzić BOM:

hd -n 3 UTF8

Jeśli BOM jest obecny, zobaczysz: 00000000 ef bb bf ...

Steve Clay
źródło
6
BOMy mają 2 bajty dla UTF-16 i 4 bajty dla UTF-32 i oczywiście nie ma powodu, aby być w UTF-8 w pierwszej kolejności.
tchrist
2
@KarolyHorvath Tak, dokładnie. Jego użycie nie jest zalecane. Niszczy rzeczy. Kodowanie powinno być określone przez protokół wyższego poziomu.
tchrist
1
@tchrist: masz na myśli zepsute rzeczy? :) odpowiednie aplikacje powinny być w stanie obsłużyć ten BOM.
Karoly Horvath
7
@KarolyHorvath Mam na myśli to, że psuje wiele programów . Czy to nie to, co powiedziałem? Po otwarciu strumienia w kodowaniu UTF-16 lub UTF-32 dekoder wie, że nie liczy BOM. Kiedy używasz UTF-8, dekodery przedstawiają BOM jako dane. To jest błąd składni w niezliczonych programach. Nawet dekoder Java zachowuje się w ten sposób, WEDŁUG PROJEKTU! BOMy na plikach UTF-8 są źle umieszczone i ból w tyłku: to błąd! Wiele rzeczy psują. Nawet po prostu cat file1.utf8 file2.utf8 file3.utf3 > allfiles.utf8się zepsuje. Nigdy nie używaj BOM w UTF-8. Kropka.
tchrist
6
hdnie jest dostępny na OS X (10.8.2) od dnia, tak aby sprawdzić, czy UTF-8 BOM nie można użyć następujących: head -c 3 file | od -t x1.
mklement0
21

Oprócz konwersji zakończeń linii CRLF na LF, dos2unixusuwa również zestawienia komponentów:

dos2unix *.txt

dos2unix konwertuje również pliki UTF-16 z BOM (ale nie pliki UTF-16 bez BOM) do UTF-8 bez 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
źródło
3

Wiem, że pytanie było skierowane do unix / linux, pomyślałem, że warto byłoby wspomnieć o dobrej opcji dla tych, którzy mają problemy z unixem (w Windows, z UI).
Napotkałem ten sam problem w projekcie WordPress (BOM powodował problemy z kanałem rss i walidacją strony) i musiałem zajrzeć do wszystkich plików w dość dużym drzewie katalogów, aby znaleźć ten, który był z BOM. Znalazłem aplikację o nazwie Replace Pioneer i w niej:

Batch Runner -> Search (aby znaleźć wszystkie pliki w podfolderach) -> Replace Template -> Binary remove BOM (jest gotowe wyszukanie i zastąpienie szablonu).

Nie było to najbardziej eleganckie rozwiązanie i wymagało zainstalowania programu, co jest wadą. Ale kiedy dowiedziałem się, co się wokół mnie dzieje, zadziałało to jak urok (i znalazłem 3 pliki z około 2300, które były z BOM).

Arnon Zamir
źródło
1
Bardzo się cieszę, gdy znalazłem Twoje rozwiązanie, jednak nie mam uprawnień do instalowania oprogramowania na firmowym komputerze. Zajęło mi to dużo czasu, zanim wymyśliłem alternatywę: Używanie Notepad ++ z wtyczką PythonScript. superuser.com/questions/418515/… Mimo wszystko dzięki!
Hoàng Long