UTF8 字元使文件無法訪問
如果我執行:
scp me@example.com:/home/me/cömmön_file.jpg /home/me/
從我的遠端伺服器我得到:
scp:/home/me/cömmön_file.jpg:沒有這樣的文件或目錄
如果我使用萬用字元換出 utf8 字元,它將起作用:
scp me@example.com:/home/me/c?mm?n_file.jpg /home/me/
和/或
scp me@example.com:/home/me/c*mm*n_file.jpg /home/me/
如果我在遠端機器上使用 AWS CLI,行為也會複製。
正如我所期望的那樣,在我的遠端機器上執行具有顯式名稱的其他命令。
例如
ls -lha /home/me/cömmön_file.jpg
-rw-r–r–。1 我我 1.1M 1 月 15 日 21:58 /home/me/cömmön_file.jpg
我也可以使用 重命名文件
mv
。傳輸文件有問題,還是我的機器託管文件有問題?
導致目前問題的 UTF8 字元是https://www.compart.com/en/unicode/U+0308>但我懷疑其他字元也會重現該問題。如果我嘗試將文件從<https://www.compart.com/en/unicode/U+00F6
ö
重命名,我的機器會告訴我文件是相同的。mv: ‘/home/me/cömmön_file.jpg’ 和 ‘/home/me/cömmön_file.jpg’ 是同一個文件
託管文件的伺服器是:
NAME="CentOS Linux" VERSION="7 (Core)"
它
locale
是:LANG=en_US.UTF-8 LC_CTYPE="en_US.UTF-8" LC_NUMERIC="en_US.UTF-8" LC_TIME="en_US.UTF-8" LC_COLLATE="en_US.UTF-8" LC_MONETARY="en_US.UTF-8" LC_MESSAGES="en_US.UTF-8" LC_PAPER="en_US.UTF-8" LC_NAME="en_US.UTF-8" LC_ADDRESS="en_US.UTF-8" LC_TELEPHONE="en_US.UTF-8" LC_MEASUREMENT="en_US.UTF-8" LC_IDENTIFICATION="en_US.UTF-8" LC_ALL=
請求文件的伺服器是:
NAME="Amazon Linux" VERSION="2"
它
locale
是:LANG=en_US.UTF-8 LC_CTYPE="en_US.UTF-8" LC_NUMERIC="en_US.UTF-8" LC_TIME="en_US.UTF-8" LC_COLLATE="en_US.UTF-8" LC_MONETARY="en_US.UTF-8" LC_MESSAGES="en_US.UTF-8" LC_PAPER="en_US.UTF-8" LC_NAME="en_US.UTF-8" LC_ADDRESS="en_US.UTF-8" LC_TELEPHONE="en_US.UTF-8" LC_MEASUREMENT="en_US.UTF-8" LC_IDENTIFICATION="en_US.UTF-8" LC_ALL=
快速解決方案:不要在鍵盤上使用重音字母,而是使用 tab-complete(並設置您的 SSH 密鑰,以便 tab-complete 也可以通過網路使用
scp
,rsync
等)或回退到萬用字元,因為你經驗是正常的預期行為。它不起作用,因為您沒有鍵入相同的文件名。
看起來很瘋狂?這對你來說是 UTF-8。
更瘋狂的是:我可以用我神奇的遠端讀心通靈能力告訴你,你有一台Apple Mac。
更嚴重的是:這是您在提出問題時忘記提供的關鍵資訊,但您在輸入問題本身時不小心洩露了資訊。
在複製粘貼上面的答案時:
# echo "scp me@example.com:/home/me/cömmön_file.jpg" | hexdump -C 00000000 73 63 70 20 6d 65 40 65 78 61 6d 70 6c 65 2e 63 |scp me@example.c| 00000010 6f 6d 3a 2f 68 6f 6d 65 2f 6d 65 2f 63 6f cc 88 |om:/home/me/co..| 00000020 6d 6d 6f cc 88 6e 5f 66 69 6c 65 2e 6a 70 67 20 |mmo..n_file.jpg | 00000030 2f 68 6f 6d 65 2f 6d 65 2f 0a |/home/me/.| 0000003a
請密切注意字母“ö”的編碼方式:
6f cc 88
。一個字面的 ‘o’ 後跟一個額外的 UTF-8 程式碼點。(事實上,在我的終端上它甚至不顯示為 ‘ö’ 而是顯示為 ‘o’)當我(=Linux 使用者)輸入:
echo /home/me/cömmön_file.jpg | hexdump -C 00000000 2f 68 6f 6d 65 2f 6d 65 2f 63 c3 b6 6d 6d c3 b6 |/home/me/c..mm..| 00000010 6e 5f 66 69 6c 65 2e 6a 70 67 0a |n_file.jpg.| 0000001b
再次仔細查看 ‘ö’ 符號 :
c3 b6
,這是一個完全不同的 UTF-8 程式碼點,沒有額外的文字 ASCII。超短解釋:UTF-8 規範化(組合與分解)。
更長的解釋:
在 Unicode 中,有多種方法可以為看起來像“ö”的東西編碼。
- 第一種方式是組合字元:有一個程式碼點是從 Latin-1 (ISO/IEC 8859-1:1998) 程式碼點、Unicode 程式碼點 U+00f6 (在 UTF-8 中編碼為 c3 b6)繼承的 ‘ö’
- 第二種方式是分解字元:你首先輸出ASCII o,然後附加一個特殊的程式碼點,意思是’請在前面的字母組合一個變音符號’,Unicode程式碼點U + 0308(在UTF-8中編碼為cc 88)
it’s this combining character that enable you to do all the̫ ͨcra̎zy shit̫ ĺiͭke̬̓ ̭Z͉̒a̅l̞gͩoͤ ̤͋aṅd̲ ̹ͨallͦ ̍ͅthͅe oͅt͔̅h̦̊e̠r ͔̋dḁŕ͕k̓ ̃m͍o͉ͅñ͎͖̉s̺͑tr̰͎̈́ỏ͖ͧsi̮͂͑t̚i͙̗ės͓̊̒ ̞ͯt̗͕ẖ̈ͩá̝ṱ̟͒ ͓͐ͦl̈́ṵ̿r͈̾k̼̝ͭ̍ ̹i͖̇̈́n͚̳ ͖̗ͦt͓h̿e͖ ̌m̳͌̽a̪ͥd̺͑n͕͌̐e̿͊s͇s̘͓͊ ̗̈́ö̫́f͕̞ ͕̰̓ìṅ̠sͤ̂a̬̝̿ͪn̘ͫ͆e̜ͯ ̩͓ͣẻ͛ḽ̞̃ḓ̺r̙̦ͥͬi̫̠̔ͮt̰̓̾ͅč͕ͦḧ̞̱͖́̒̽ ͇̳ḁ̖̊̈b̏͑o̳̙̍m̩̪̞ͦi̇ͮn̳͔ͨ̏ͤa̤̯ͣṱ̰ͥï̺̄o̞͖̿n͆ͦs̬̍ ̹ͩ͒th̞̄a̗̗͐͌ͪt͂ ̬̞iͭ̒s̘͇ ̱̯̐̆̒Ũ̺̞̘ͯT̩̀̔̚F̪͒̄-̪̘̈́8̮̆̍͂.̱͍̂
哼。
地球上的其他地方盡可能使用組合字元(因為它更緊湊,也因為它使用與 Latin-1 兼容的 Unicode 範圍,簡化了向後兼容性)並且只對沒有它們的東西使用組合字元自己的程式碼點(主要是不太常見的語言)。
Apple 顯然生活在另一個星球上,他們決定嘗試始終使用組合字元(因為他們崇拜黑暗領主 Za͓̙̘͌l̦̖͉̃ͦ͆͊ͧ̀g͖̭̼̗͉̦̬̍̀̌ͬ̓ͥ҉o̧͉̗̱̥̣̯͍̗̲̩̼̗͉̦̬ͪ͋̓͑̈́ͦͦ̐̓͑̈́ͦ̐̓̓͑̈́ͦ̐̍̀̌ͬ̓ͥ͘҉o̧͉̗̱̥̣̯͍̗̲̩ͦ̐̓͑̈́ͦ̐̓͑̈́ͦ̐̓͊̈́ͦ͘
鍵入看起來像“ö”的鍵盤字母根本不會生成相同的二進制序列,具體取決於您鍵入鍵的電腦。
然後開始發揮作用:大多數 Unix 傾向於使用對大小寫敏感且對 Unicode 編碼敏感(支持 UTF-8)的文件系統(如 Linux 的 EXT4)。他們試圖保留文本是否被撰寫。因此,它們區分了 UTF-8 二進制序列
6f cc 88
,c3 b6
即使它們為相同的最終結果“ö”編碼。(以同樣的方式區分“A”和“a”,即使它是相同的拉丁字母)。所以你的鍵盤產生的“ö”和伺服器上的“ö”是不一樣的。碰巧堆棧交換只是按原樣儲存您扔給它的任何 Unicode 編碼,從而導致作為HTML RegEx 解析器的神話答案。(因此,您的 Mac 被記錄“ö”的特定字節序列出賣了自己)。