2015-11-28 42 views
14

我正在嘗試編寫一個腳本,它將自動從文件中刪除UTF-8 BOM。我無法檢測文件是否具有第一位。這裏是我的代碼:如何檢測文件是否在Bash中有UTF-8 BOM?

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

出於某種原因,head似乎被忽略了BOM在文件的前面。作爲一個例子,運行這個

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

將不會打印任何東西。

我試過在head --help尋找一個選項,可以讓我解決這個問題,但沒有運氣。我能做些什麼來完成這項工作?

回答

11

首先,讓我們證明head實際工作正常:

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

現在,讓我們創建一個工作功能has_bom。如果您grep支持-P,那麼一種選擇是:

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

目前,只有GNU grep支持-P

另一種選擇是使用bash的$'...'

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

kshzsh也支持$'...'但這個結構不是POSIX和dash不支持它。

注:

  1. 使用一個明確的return $?是可選的。該函數默認情況下會返回上一個命令運行的退出代碼。

  2. 我已經使用POSIX表格來定義函數。這相當於bash窗體,但是如果您需要在另一個shell下運行該功能,則可以減少處理的一個問題。

  3. bash確實接受在函數名稱中使用字符-,但這是一個有爭議的特性。我用更爲廣泛接受的_取而代之。 (有關這方面的問題,請參閱this answer

  4. -q選項grep使它安靜的,這意味着它仍然設置一個適當的退出代碼,但它不發送任何字符到標準輸出。

+1

呵呵,從來不知道Bash支持十六進制字符串文字。無論如何,謝謝你的偉大答案! –

+0

您好,請問我行'head -c 3 file | hexdump -c','-c'做什麼?前一個似乎是1)限制字符輸出的數量2)限制行號(可能)爲0000000和0000003;但是後者會將輸出結果視爲「be bf」等等,置換爲替換標記。我使用bash和Windows下生成的文本文件進行測試,原始編碼= GB18030。謝謝。 – CrazyFrog

+0

@CrazyFrog'head -c 3 file'將'file'的前三個字符寫入標準輸出。 'hexdump -C'將這些字符以人性化的方式格式化爲十六進制。 – John1024

0

我施加的以下對於第一讀取線:

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

這僅僅去除該變量中的BOM。

相關問題