2015-11-28 17 views
14

Próbuję napisać skrypt, który automatycznie usunie BOM z UTF-8 z pliku. Mam problem z wykryciem, czy plik ma pierwszeństwo, czy nie. Tu jest mój kodu:Jak wykryć, czy plik ma BOM UTF-8 w Bash?

function has-bom { 
    # Test if the file starts with 0xEF, 0xBB, and 0xBF 
    head -c 3 "$1" | grep -P '\xef\xbb\xbf' 
    return $? 
} 

Z jakiegoś powodu, head wydaje się być ignorowanie LM przed pliku. Na przykład, uruchomienie tego nie będzie niczego drukować.

Próbowałem szukać opcji w head --help, która pozwoliłaby mi obejść to, ale bez powodzenia. Czy jest coś, co mogę zrobić, aby to zadziałało?

Odpowiedz

11

Najpierw wykazać, że head rzeczywiście działa poprawnie:

$ printf '\xef\xbb\xbf' >file 
$ head -c 3 file 
$ head -c 3 file | hexdump -C 
00000000 ef bb bf           |...| 
00000003 

Teraz stwórzmy funkcję roboczą has_bom. Jeśli grep obsługuje -P, a następnie jedną z opcji jest:

$ has_bom() { head -c3 "$1" | LC_ALL=C grep -qP '\xef\xbb\xbf'; } 
$ has_bom file && echo yes 
yes 

Obecnie tylko GNU grep obsługuje -P.

Inną opcją jest użycie bash na $'...':

$ has_bom() { head -c3 "$1" | grep -q $'\xef\xbb\xbf'; } 
$ has_bom file && echo yes 
yes 

ksh i zsh również wspierać $'...' ale ta konstrukcja nie jest POSIX i dash nie obsługuje.

Uwagi:

  1. Zastosowanie wyraźnego return $? jest opcjonalna. Funkcja domyślnie powróci z kodem zakończenia ostatniego uruchomienia polecenia.

  2. Korzystałem z formularza POSIX do definiowania funkcji. Jest to odpowiednik formularza bash, ale daje jeden mniejszy problem do rozwiązania, jeśli kiedykolwiek będziesz musiał uruchomić tę funkcję w innej powłoce.

  3. bash akceptuje użycie znaku - w nazwie funkcji, ale jest to kontrowersyjna funkcja. Wymieniłem go na _, który jest szerzej akceptowany. (Więcej na ten temat można znaleźć this answer.)

  4. Opcja grep-q sprawia, że ​​jest ciche, co oznacza, że ​​nadal ustawia odpowiedni kod wyjścia, ale nie wysyła żadnych znaków na standardowe wyjście.

+1

Huh, nigdy nie wiedziałem, że Bash obsługuje ciągi liter szesnastkowych. W każdym razie, dzięki za wspaniałą odpowiedź! –

+0

cześć, mogę zapytać w wierszu 'head -c 3 file | hexdump -c ', co robi' -c'?Wydaje się, że poprzednia 1) ograniczenie liczby znaków wyjściowych 2) ograniczyć liczbę linii (być może) do 0000000 i 0000003; ale ta ostatnia sprawia, że ​​wyjście, które ma być "be bf" itp., na marker zastępczy. Używam bash i testing na pliku tekstowym wygenerowanym pod Windows, oryginalne kodowanie = GB18030. Dzięki. – CrazyFrog

+0

@ CrazyFrog 'head -c 3 file' zapisuje pierwsze trzy znaki' file' na standardowe wyjście. 'hexdump -C' formatuje te znaki w przyjazny dla człowieka sposób w postaci szesnastkowej. – John1024

0

Zgłosiłem co nastepuje na pierwszej linii odczytu:

read c 
if (("$(printf "%d" "'${c:0:1}")" == 65279)) ; then c="${c:1}" ; fi 

To po prostu usuwa LM od zmiennej.

Powiązane problemy