Przykładowa komenda, która wykazuje objaw: sed 's/./@/' <<<$'\xfc'
kończy się niepowodzeniem, ponieważ bajt 0xfc
nie jest prawidłowym znakiem UTF-8.
Zauważ, że dla kontrastu, GNU sed
(Linux, ale także instalowalny na macOS) po prostu przesyła nieprawidłowy bajt bez zgłaszania błędu.
Użycie poprzednio przyjętej odpowiedzi jest opcją, jeśli nie masz nic przeciwko utracie wsparcia dla prawdziwych ustawień regionalnych (jeśli korzystasz z systemu amerykańskiego i nigdy nie musisz zajmować się obcymi postaciami, może to być w porządku).
Jednak sam efekt można było ad-hoc dla pojedynczego polecenia tylko :
LC_ALL=C sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure
Uwaga: Liczy się to skuteczne LC_CTYPE
ustawienie C
, tak LC_CTYPE=C sed ...
by normalnie też praca, ale jeśli LC_ALL
dzieje się zestaw (do czegoś innego niż C
), to zastąpi poszczególne LC_*
zmienne -category takie jak LC_CTYPE
. Zatem najbardziej niezawodnym podejściem jest ustawienie LC_ALL
.
Jednak (skutecznie) ustawienie LC_CTYPE
do C
traktuje ciągi jakby każdy bajt był jego własny charakter ( nie interpretacja w oparciu o zasady kodowania jest wykonana), ze bez względu na - wielobajtowych-on-demand - kodowanie UTF-8 , że OS X wykorzystuje domyślnie , w których znaki obce mają kodowanie wielobajtowe .
W skrócie: ustawienie LC_CTYPE
doC
przyczyn skorupę i narzędzia do rozpoznawania tylko podstawowe litery angielskich jak listy (te w 7-bitowego zakresu ASCII), dzięki czemu obcych znaków. nie będą traktowane jak litery , co spowoduje na przykład konwersję wielkich / małych liter.
Ponownie, może to być w porządku, jeśli nie musisz dopasowywać znaków zakodowanych w wielobajtach, takich jak é
, i po prostu chcesz przepuścić takie znaki .
Jeśli jest to niewystarczające i / lub chcesz zrozumieć przyczynę pierwotnego błędu (w tym określić, które bajty wejściowe spowodowały problem) i wykonać konwersje kodowania na żądanie, przeczytaj poniżej.
Problem polega na tym, że kodowanie pliku wejściowego nie jest zgodne z kodowaniem powłoki.
Mówiąc dokładniej, plik wejściowy zawiera znaki zakodowane w sposób, który nie jest poprawny w UTF-8 (jak stwierdził @Klas Lindbäck w komentarzu) - to właśnie sed
próbuje przekazać komunikat o błędzie invalid byte sequence
.
Najprawdopodobniej plik wejściowy używa 8-bitowego kodowania jednobajtowego, na przykład ISO-8859-1
często używanego do kodowania języków „zachodnioeuropejskich”.
Przykład:
Akcentowana litera à
ma kod Unicode 0xE0
(224) - taki sam jak w ISO-8859-1
. Jednak ze względu na naturę kodowania UTF-8 ten pojedynczy punkt kodowy jest reprezentowany jako 2 bajty - 0xC3 0xA0
podczas gdy próba przekazania pojedynczego bajtu 0xE0
jest nieprawidłowa w UTF-8.
Oto demonstracja problemu przy użyciu łańcucha voilà
zakodowanego jako ISO-8859-1
, z à
reprezentowanym jako jeden bajt (za pomocą ciągu bash cytowanego w ANSI-C ( $'...'
), który używa \x{e0}
do utworzenia bajtu):
Zauważ, że sed
polecenie to faktycznie nie działa, po prostu przekazuje dane wejściowe, ale potrzebujemy go, aby wywołać błąd:
# -> 'illegal byte sequence': byte 0xE0 is not a valid char.
sed 's/.*/&/' <<<$'voil\x{e0}'
Aby po prostu zignorować problem , LCTYPE=C
można zastosować powyższe podejście:
# No error, bytes are passed through ('á' will render as '?', though).
LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}'
Jeśli chcesz ustalić, które części danych wejściowych powodują problem , spróbuj wykonać następujące czynności:
# Convert bytes in the 8-bit range (high bit set) to hex. representation.
# -> 'voil\x{e0}'
iconv -f ASCII --byte-subst='\x{%02x}' <<<$'voil\x{e0}'
Dane wyjściowe pokażą wszystkie bajty, które mają ustawiony wysoki bit (bajty przekraczające 7-bitowy zakres ASCII) w postaci szesnastkowej. (Należy jednak pamiętać, że obejmuje to również poprawnie zakodowane wielobajtowe sekwencje UTF-8 - potrzebne byłoby bardziej wyrafinowane podejście do konkretnej identyfikacji bajtów typu „nieprawidłowa w UTF-8”).
Przeprowadzanie konwersji kodowania na żądanie :
Standardowego narzędzia iconv
można użyć do konwersji na kodowanie ( -t
) i / lub z ( -f
); iconv -l
wyświetla wszystkie obsługiwane.
Przykłady:
Konwertuj FROM ISO-8859-1
na kodowanie obowiązujące w powłoce (na podstawie LC_CTYPE
, która jest UTF-8
domyślnie oparta na) , bazując na powyższym przykładzie:
# Converts to UTF-8; output renders correctly as 'voilà'
sed 's/.*/&/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')"
Pamiętaj, że ta konwersja pozwala odpowiednio dopasować obce znaki :
# Correctly matches 'à' and replaces it with 'ü': -> 'voilü'
sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')"
Aby przekonwertować wejściowy BACK na ISO-8859-1
przetworzony, po prostu potokuj wynik do innego iconv
polecenia:
sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')" | iconv -t ISO-8859-1