我在USB驱动器上有来自Windows-PC的文件,其文件名中有特殊字符。在macOS上,这是一个问题,因为以下行为:
% ls
(不含-a
)可以看到- 文件可以被
% find .
看到 - 文件不能用
rm
删除 - 包含文件夹的所有内容都可以被删除
- 在Finder.app 中不可见
- 包含的文件夹不能在Finder中复制,停止并出现错误信息
The operation can’t be completed because one or more required items can’t be found. (Error code -43)
- 不能用
% mv
重命名,出现No such file or directory
- 我可以在macOS中创建一个同名的文件
- 文件在Windows 10上正常工作
- 在macOS中删除包含的文件夹会显示它们在垃圾桶中,但是垃圾桶不能清空
在Windows上,这些文件可以完美地工作,并且可以重命名,然后它们可以在macOS上工作。
问题是文件名中有包含两个unicode码点的字符。如果我在USB驱动器上的Windows-PC上编辑文件名,我需要删除两次以摆脱ü,因为它由u和¨组成。
的例子:
在使用FAT32或ExFAT格式化的u盘上,用两个unicode码点组合的字符命名文件:U+0075
后跟U+0308
(Windows)。
% ls
ü.txt
% mv ü.txt test.txt
mv: rename ü.txt to test.txt: No such file or directory
如何在macOS上重命名文件?
USB驱动器格式化为FAT32,我也尝试过ExFAT,同样的损坏行为。macOS被格式化为APFS
因为我希望将来我得到的一些文件也有这样的文件名,我想避免为了这个唯一的目的而需要一台PC。
Z Shell示例脚本
我试着用hc_dev
的答案来创建一个工作脚本。
# Filename is removeUTF16inFilename.zsh
# A script to remove any UTF-16 characters from the file name and adding one ASCII character in case the file name only consists of UTF-16 characters.
# Issue: Windows uses UTF-16 for encoding file-names. But macOS uses UTF-8 in Unicode NFD (Normalization Form Canonical Decomposition).
# Usage: Copy the script in the folder with the problematic files, then run the script with % zsh removeUTF16inFilename.zsh
count=1
for file in *
do
if [[ $file != "removeUTF16inFilename.zsh" ]]
then
echo "File number: $count"
echo "File name: $file"
newFilename=$(echo $file | tr -cd 'A-Za-z0-9_.-')
mv $file $count$newFilename
echo "Renaming $file to $count$newFilename"
((count++))
echo
fi
done
仍然错误No such file or directory
.
Swift 5示例脚本
import Foundation
let fileManager = FileManager.default
let currentDirectoryPath = fileManager.currentDirectoryPath
do {
let contentsOfDirectory = try fileManager.contentsOfDirectory(atPath: currentDirectoryPath)
var count = 1
for filename in contentsOfDirectory {
if filename == "removeUTF16inFilename.swift" {
continue
}
print("File number: (count)")
print("Filename: (filename)")
let newFilename = String(count) + String(filename.unicodeScalars.filter{ $0.isASCII })
print("Renaming file to (newFilename) n")
let filePathOld = currentDirectoryPath + "/" + filename
let filePathNew = currentDirectoryPath + "/" + newFilename
try fileManager.moveItem(atPath: filePathOld, toPath: filePathNew)
count = count + 1
}
} catch {
print(error.localizedDescription)
}
print("Script finished")
:
% pwd
/Volumes/BLACK/test
% ls -l
total 416
-rwx------@ 1 meuser staff 947 Dec 30 14:15 removeUTF16inFilename.swift
-rwx------ 1 meuser staff 88836 Nov 18 2020 ü.pdf
-rwx------ 1 meuser staff 0 Dec 30 10:46 ü.txt
% swift removeUTF16inFilename.swift
File number: 1
Filename: ü.txt
Renaming file to 1u.txt
“ü.txt” couldn’t be moved to “test” because either the former doesn’t exist, or the folder containing the latter doesn’t exist.
Script finished
Hexdump
735 14:51 % pwd
/Volumes/BLACK/test
736 14:51 % ls
ü.txt
737 14:51 % ls -l | hexdump -C
00000000 74 6f 74 61 6c 20 30 0a 2d 72 77 78 2d 2d 2d 2d |total 0.-rwx----|
00000010 2d 2d 20 20 31 20 6e 69 74 72 6f 20 20 73 74 61 |-- 1 nitro sta|
00000020 66 66 20 20 30 20 44 65 63 20 33 30 20 31 30 3a |ff 0 Dec 30 10:|
00000030 34 36 20 75 cc 88 2e 74 78 74 0a |46 u...txt.|
0000003b
748 14:59 % ls
ü.txt
749 14:59 % ls | hexdump -C
00000000 75 cc 88 2e 74 78 74 0a |u...txt.|
00000008
750 14:59 % echo .txt | hexdump -C
00000000 2e 74 78 74 0a |.txt.|
00000005
0a
是结尾,所以有问题的ü
一定是75 cc 88
。ü
在macOS上是c3 bc
.
在macOS中创建的名为ü.txt
的文件和在Windows中创建的名为ü.txt
的错误文件的Hexdump。
% ls
ü.txt ü.txt
ls | hexdump -C
00000000 75 cc 88 2e 74 78 74 0a 75 cc 88 2e 74 78 74 0a |u...txt.u...txt.|
00000010
printf
也不工作
762 15:17 % ls
ü.txt
763 15:18 % ls | hexdump -C
00000000 75 cc 88 2e 74 78 74 0a |u...txt.|
00000008
764 15:18 % cp $(printf "x75xccx88").txt uuuuu.txt
cp: ü.txt: No such file or directory
示例脚本
脚本中的第一个命令仅在PowerShell中复制和使用时有效,而在脚本中运行时无效。Visual Studio Code
在Windows和macOS上保留Unicode字符。大多数应用程序自动将字符从U+0075
后面跟着U+0308
转换为U+00FC
,例如Xcode
和TextEdit
。
从这里下载:workupload
Issue
Windows使用UTF-16用于编码文件名。但是macOS在UnicodeNFD(规范化形式规范分解)中使用UTF-8。
:
- osx mavericks - Windows与Mac OS X的Unicode文件名-超级用户
- macos -文件名中不同的utf8编码os x -堆栈溢出
- 终端-如何剥特殊字符的文件名?-询问不同的
重命名使用tr -cd
删除UTF-16字符
根据AskDifferent的回答建议,使用具有有限字符集(例如ASCII)的翻译命令。与mv
结合使用,可以删除目标文件名中所有不需要的字符,如UTF-16。
在激活它之前尝试在这个模拟中重命名文件:
for file in *; do echo mv "$file" `echo $file | tr -cd 'A-Za-z0-9_.-'` ; done
注意:要激活重命名,请删除阻止的"echo "在mv
命令前面。
使用索引节点编号
像许多Unix/Linux系统一样,BSD也有inode的概念。要显示文件的索引节点,使用-i
标志和ls
:
ls -i
12382580 _ü.txt
然后可以使用find * -inum 12382580
再次选择文件并传递下一个命令,例如使用-exec
,其他命令选项或-print
到xargs
的管道(分别为-print0
到xargs -0
)。
参见:
- 唯一标识文件
- 文件系统—当用NFS挂载时,我如何在一个目录中有两个同名的文件?-超级用户