[英]Globbing accented files in Bash
我正在嘗試驗證Bash
是否存在文件。 我知道文件名(在變量中)但不知道擴展名(可以是.pmdl
或.umdl
)。
在 OSX 上,這有效:
$> ls
ecole.pmdl
$> filename="ecole"
$> ls "$filename."[pu]mdl
ecole.pmdl
但是當文件名包含重音時它不會:
$> ls
école.pmdl
$> filename="école"
$> ls "$filename."[pu]mdl
ls: école.[pu]mdl: No such file or directory
但是,如果我不使用通配符,它會起作用:
$> ls "$filename."pmdl
école.pmdl
我正在尋找一個適用於 Linux 和 OSX 的簡單解決方案。 這是我在該主題上找到的最接近的問題。
編輯:
$> bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin16)
Copyright (C) 2007 Free Software Foundation, Inc.
編輯2:
在 OSX Bash v3.2.57 上使用相同的é
char 證明該場景(系統地)失敗的簡短版本。 Linux Bash 4.3.30 上的相同場景系統地工作(找到)。
$> touch é.txt
$> ls é*
ls: é*: No such file or directory
HFS+ here and here (Apple 文件系統)要求以分解形式存儲 Unicode 字符串(而不是預先組合的字符)。
然后將Unicode碼位U+0E9的é
這樣的字符分解為Unicode碼位U+065和U+0301的兩個字符e
和´
。
您可以通過創建一個干凈的空目錄並執行以下操作來查看此差異:
$ a='é'
$ echo "$a" >.text
$ touch "$a"
$ ls > .list
然后比較這兩個命令的輸出:
$ od -vAn -tx1c .text
c3 a9 0a
303 251 \n
$ od -vAn -tx1c .list
65 cc 81 0a
e 314 201 \n
哪個不相等。
您可以嘗試在您的系統中使用此模式:
ls "e$(echo -e '\xcc\x81')cole".[pu]mdl
這只是文件系統中é
由兩個字符表示的表達式。
了解此問題已在較新的 bash 版本中解決。
參考:
tl;博士
要么:使用以下解決方法之一:
ls "$(iconv -t UTF-8-MAC <<<'école')."[pu]mdl
- 最通用,但很麻煩。ls $'e\\x{cc}\\x{81}cole'.[pu]mdl
- 難以記住,並且特定於手頭的變音符號(尖銳的口音, ´
)。ls e?cole.[pu]mdl
- 輸入和記憶簡單,但僅限於 1 個組合變音符號,可能會產生誤報。 或者:通過Homebrew安裝 Bash 4.3.30 或更高版本並使用它代替 macOS 仍然附帶的 Bash 3.x: brew install bash
。
血腥細節如下。
對於非 ASCII 字符,
macOS 文件系統HFS+僅使用NFD (分解的Unicode 規范化形式),其中重音字母由2 個或更多Unicode 代碼點表示:基本字母,后跟組合變音符號(重音符號):
é
的情況下:
e
( U+0065
, UTF-8 編碼0x65
)´
U+0301
,UTF-8 編碼0xcc 0x81
)。Ṹ
的情況。然而,通常- 例如當您在終端或大多數編輯器中鍵入字符時 - 使用NFC (組合Unicode 規范化形式),其中(習慣的)帶重音的字母由1 個Unicode 代碼點表示:
é
的情況下:單個Unicode 字符U+00E9
,UTF-8 編碼0xc3 0xa9
。ls école
,表示為 NFC,確實找到了名為école
的文件,該文件存儲在 NFD 中 - 大概是因為 Bash 只是將 NFC 表示傳遞給了一個系統函數,該函數可以識別等價性。在此處閱讀這些Unicode 標准(規范化)形式。
簡而言之:從 macOS 10.12.1 附帶的過時版本 - Bash 3.2.57 開始,Bash應該將 NFD 和 NFC 表示識別為等效的,但不會。
雖然在 macOS 上運行時該問題至少從 Bash 4.3.30 開始得到解決,但出於許可原因, Apple 並未更新到 Bash 4.x版本(請參閱下文以獲取解決方案)。
請參閱本文底部以了解Linux世界。
有在 macOS 上使用重音字符通配文件名的解決方法:
[如果可行] 使用Homebrew ,安裝最新的 4.x Bash 版本並使用它而不是 macOS 附帶的版本: brew install bash
。
[健壯,但更詳細]使用iconv -t UTF-8-MAC
將您的 Bash 字符串文字從 NFC 轉換為 NFD,使其與文件系統表示相匹配:
ls "$(iconv -t UTF-8-MAC <<<'école')."[pu]mdl
ls $'e\\x{cc}\\x{81}cole'.[pu]mdl
[更簡單,但次優] 將每個重音字符表示為<base-char>?
,因為從 Bash 的角度來看,文件系統報告的重音字符等於基本字符e
后跟另一個字符(組合變音符號;針對多個組合變音符號進行相應調整)。 (這種做法顯然是不理想的,因為它不會只匹配é
,但任何兩個字符序列開始e
):
ls e?cole.[pu]mdl
許多Linux發行版使用的ext文件系統完全按照指定的方式存儲文件名:
換句話說:使用 NFC 名稱創建的文件將按原樣存儲,具有 NFD 名稱的文件也是如此。
因此, ext
考慮了 NFC 和 NFD 不同的形式,因為它們的字節級表示不同,所以它甚至允許(概念上)相同名稱的文件僅在 Unicode 范式中不同——例如,名為$'e\\xcc\\x81cole'
和$'\\xc3\\xa9cole'
由ls
( école
) 打印時無法區分,但它們是不同的文件 (!)。
因此 - 並且適當地 - Linux上的 Bash 版本不識別 NFC / NFD 等效性,即使在版本 >= 4.3.30(與 macOS 不同)。
警告: dash
,它在 Ubuntu 上充當/bin/sh
,例如,從 Ubuntu 16.04 開始,它不是區域設置感知的(多字節字符編碼感知),至少在globbing 時: globbing symbol ?
匹配單個字節而不是單個字符(由活動語言環境的字符編碼定義,反映在語言環境類別LC_CTYPE
,通常是 UTF-8)。 因此,為了匹配單個非 ASCII 字符,您需要知道該字符的 UTF-8 編碼由多少個字節組成,並使用?
對於每個字節; 例如,NFC é
(2 個字節)必須與??
. [1]
當您在 shebang 行為#!/bin/sh
腳本中使用通配符時,這可能很重要。
在實踐中,NFD 字符串很少遇到,因此使用 NFC 字符串創建文件並稍后通過 glob 匹配它們,macOS 遇到的不同 Unicode 范式的問題很少在 Linux 上出現。
[1] dash
旨在成為一個快速的、符合 POSIX 標准的 shell 實現(主要限於POSIX 功能),但在這種情況下它似乎達不到: POSIX 規范的一部分。 描述模式匹配符號清楚地談論字符,而不是字節: A <question-mark> is a pattern that shall match any character.
字符集部分描述了對多字節字符編碼的支持。
$ echo "école." | xxd
00000000: c3a9 636f 6c65 0a ..cole.
$ echo "école." | xxd
00000000: 65cc 8163 6f6c 650a e..cole.
因此,由此我們可以看到它們是不同的字符:
$ echo -e "\x65\xCC\x81"
é
$ echo -e "\xC3\xA9"
é
您在文件名中使用的字符與變量中設置的字符不同。
for i in {1..3}; do f="école"; ls "$f."[pu]mdl; echo "$i: $f."[pu]mdl; done
for i in {1..3}; do f="école"; ls "$f."[pu]mdl; echo "$i: $f."[pu]mdl; done
ls: école.[pu]mdl: No such file or directory
1: école.[pu]mdl
ls: école.[pu]mdl: No such file or directory
2: école.[pu]mdl
ls: école.[pu]mdl: No such file or directory
3: école.[pu]mdl
école.pmdl
1: école.[pu]mdl
école.pmdl
2: école.[pu]mdl
école.pmdl
3: école.[pu]mdl
這個錯誤很難重現,因為將字符從一個地方復制並粘貼到另一個地方可能會被編輯器、shell 等轉換,從而完全改變它。 它可能看起來像同一個角色,但由於看似無法區分的細節,它確實有所不同。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.