2015-11-28 16 views
14

Tôi đang cố gắng viết một tập lệnh sẽ tự động xóa các BOM UTF-8 khỏi một tệp. Tôi gặp sự cố khi phát hiện xem tệp có ở vị trí đầu tiên hay không. Đây là mã của tôi:Làm thế nào để phát hiện xem tệp có một UTF-8 BOM trong Bash không?

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

Đối với một số lý do, head dường như bị bỏ qua BOM ở phía trước của tập tin. Ví dụ: chạy số này

printf '\xef\xbb\xbf' > file 
head -c 3 file 

sẽ không in bất kỳ thứ gì.

Tôi đã thử tìm kiếm một tùy chọn trong head --help để tôi có thể làm việc này, nhưng không may mắn. Tôi có thể làm gì để thực hiện công việc này không?

Trả lời

11

Đầu tiên, chúng ta hãy chứng minh rằng head đang thực sự làm việc một cách chính xác:

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

Bây giờ, chúng ta hãy tạo một hàm làm việc has_bom. Nếu bạn grep hỗ trợ -P, sau đó một lựa chọn là:

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

Hiện nay, chỉ GNU grep hỗ trợ -P.

Một lựa chọn khác là sử dụng của $'...' bash:

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

kshzsh cũng hỗ trợ $'...' nhưng cấu trúc này không phải là POSIX và dash không hỗ trợ nó.

Ghi chú:

  1. Việc sử dụng một rõ ràng return $? là không bắt buộc. Theo mặc định, hàm sẽ trả về bằng mã thoát của lệnh cuối cùng.

  2. Tôi đã sử dụng biểu mẫu POSIX để xác định các chức năng. Điều này tương đương với biểu mẫu bash nhưng cung cấp cho bạn một vấn đề ít hơn để giải quyết nếu bạn phải chạy hàm dưới một trình bao khác.

  3. bash chấp nhận việc sử dụng ký tự - trong tên hàm nhưng đây là một tính năng gây tranh cãi. Tôi đã thay thế nó bằng _ được chấp nhận rộng rãi hơn. (Để biết thêm về vấn đề này, hãy xem this answer.)

  4. Tùy chọn -q làm cho nó yên tĩnh, có nghĩa là nó vẫn đặt mã thoát thích hợp nhưng không gửi bất kỳ ký tự nào.

+1

Huh, không bao giờ biết Bash hỗ trợ chuỗi ký tự hex. Dù sao, cảm ơn cho câu trả lời tuyệt vời! –

+0

hi, tôi có thể hỏi trong dòng 'head -c 3 file | hexdump -c ',' -c' làm gì?Cái trước có vẻ là 1) giới hạn số ký tự đầu ra 2) giới hạn số dòng (có thể) đến 0000000 và 0000003; nhưng sau đó làm cho đầu ra, được cho là "được bf" vv, vào điểm đánh dấu thay thế. Tôi đang sử dụng bash và thử nghiệm trên một tập tin văn bản được tạo ra trong Windows, mã hóa ban đầu = GB18030. Cảm ơn. – CrazyFrog

+0

@CrazyFrog 'head -c 3 file' ghi ba ký tự đầu tiên của' tệp' thành tiêu chuẩn. 'hexdump -C' định dạng những ký tự đó theo cách thân thiện với con người dưới dạng thập lục phân. – John1024

0

tôi áp dụng các nội dung sau cho dòng đọc đầu tiên:

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

này chỉ đơn giản là loại bỏ các BOM từ biến.

Các vấn đề liên quan