Một lệnh mẫu thể hiện triệu chứng: sed 's/./@/' <<<$'\xfc'
thất bại, bởi vì byte 0xfc
không phải là char UTF-8 hợp lệ.
Lưu ý rằng, ngược lại, GNU sed
(Linux, nhưng cũng có thể cài đặt trên macOS) chỉ đơn giản chuyển byte không hợp lệ qua mà không báo cáo lỗi.
Sử dụng câu trả lời được chấp nhận trước đây là một tùy chọn nếu bạn không mất việc hỗ trợ cho địa điểm thực sự của mình (nếu bạn đang sử dụng hệ thống ở Hoa Kỳ và bạn không bao giờ cần phải xử lý các ký tự nước ngoài, điều đó có thể ổn.)
Tuy nhiên, hiệu ứng tương tự có thể chỉ có đặc biệt cho một lệnh duy nhất :
LC_ALL=C sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure
Lưu ý: Điều quan trọng là hiệu quả LC_CTYPE
thiết lập của C
, vì vậy LC_CTYPE=C sed ...
sẽ thường cũng làm việc, nhưng nếu LC_ALL
xảy ra là bộ (một cái gì đó khác hơn C
), nó sẽ ghi đè cá nhân LC_*
biến -Danh mục như LC_CTYPE
. Vì vậy, cách tiếp cận mạnh mẽ nhất là thiết lập LC_ALL
.
Tuy nhiên, cài đặt (một cách hiệu quả) LC_CTYPE
để C
xử lý các chuỗi như thể mỗi byte là ký tự riêng của nó ( không có giải thích dựa trên quy tắc mã hóa nào được thực hiện), không liên quan đến mã hóa UTF-8 theo yêu cầu - mặc định mà OS X sử dụng theo mặc định , nơi các ký tự nước ngoài có mã hóa đa bào .
Tóm lại: cài đặt LC_CTYPE
đểC
làm cho trình bao và các tiện ích chỉ nhận ra các chữ cái tiếng Anh cơ bản là các chữ cái (các ký tự trong phạm vi ASCII 7 bit), sao cho các ký tự nước ngoài. sẽ không được coi là chữ cái , ví dụ, chuyển đổi chữ hoa / chữ thường không thành công.
Một lần nữa, điều này có thể ổn nếu bạn không cần phải khớp các ký tự được mã hóa đa chuỗi như é
, và chỉ đơn giản là muốn chuyển các ký tự đó qua .
Nếu điều này là không đủ và / hoặc bạn muốn hiểu nguyên nhân gây ra lỗi ban đầu (bao gồm cả việc xác định byte đầu vào nào gây ra sự cố) và thực hiện chuyển đổi mã hóa theo yêu cầu, hãy đọc phần bên dưới.
Vấn đề là mã hóa của tệp đầu vào không khớp với shell.
Cụ thể hơn, tệp đầu vào chứa các ký tự được mã hóa theo cách không hợp lệ trong UTF-8 (như @Klas Lindbäck đã nêu trong một nhận xét) - đó là những gì mà thông sed
báo lỗi đang cố nói invalid byte sequence
.
Rất có thể, tệp đầu vào của bạn sử dụng mã hóa 8 bit một byte như ISO-8859-1
, thường được sử dụng để mã hóa các ngôn ngữ "Tây Âu".
Thí dụ:
Chữ có dấu à
có bảng mã Unicode 0xE0
(224) - giống như trong ISO-8859-1
. Tuy nhiên, do bản chất của mã hóa UTF-8 , mã hóa đơn này được biểu diễn dưới dạng 2 byte - 0xC3 0xA0
trong khi cố gắng vượt qua byte đơn 0xE0
là không hợp lệ theo UTF-8.
Dưới đây là một minh chứng cho vấn đề sử dụng chuỗi voilà
được mã hóa dưới dạng ISO-8859-1
, với à
đại diện là một byte (thông qua chuỗi bash ( $'...'
) được sử dụng \x{e0}
để tạo byte):
Lưu ý rằng sed
lệnh thực sự là một lệnh cấm đơn giản chỉ cần chuyển đầu vào qua, nhưng chúng ta cần nó để gây ra lỗi:
# -> 'illegal byte sequence': byte 0xE0 is not a valid char.
sed 's/.*/&/' <<<$'voil\x{e0}'
Để đơn giản bỏ qua vấn đề , LCTYPE=C
cách tiếp cận trên có thể được sử dụng:
# No error, bytes are passed through ('á' will render as '?', though).
LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}'
Nếu bạn muốn xác định phần nào của đầu vào gây ra sự cố , hãy thử các cách sau:
# 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}'
Đầu ra sẽ hiển thị cho bạn tất cả các byte có tập bit cao (byte vượt quá phạm vi ASCII 7 bit) ở dạng thập lục phân. (Tuy nhiên, lưu ý rằng điều đó cũng bao gồm các chuỗi đa bào UTF-8 được mã hóa chính xác - sẽ cần một cách tiếp cận phức tạp hơn để xác định cụ thể các byte không hợp lệ trong UTF-8.)
Thực hiện chuyển đổi mã hóa theo yêu cầu :
Tiện ích tiêu chuẩn iconv
có thể được sử dụng để chuyển đổi sang mã hóa ( -t
) và / hoặc từ ( -f
); iconv -l
liệt kê tất cả những người được hỗ trợ.
Ví dụ:
Chuyển đổi TỪ ISO-8859-1
sang mã hóa có hiệu lực trong shell (dựa trên LC_CTYPE
, được UTF-8
dựa trên mặc định), dựa trên ví dụ trên:
# Converts to UTF-8; output renders correctly as 'voilà'
sed 's/.*/&/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')"
Lưu ý rằng chuyển đổi này cho phép bạn khớp đúng các ký tự nước ngoài :
# Correctly matches 'à' and replaces it with 'ü': -> 'voilü'
sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')"
Để chuyển đổi BACK đầu vào thành ISO-8859-1
sau khi xử lý, chỉ cần chuyển kết quả sang iconv
lệnh khác :
sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')" | iconv -t ISO-8859-1