我有一堆目录和子目录,其中包含带有特殊字符的文件,例如以下文件:
robbie@phil:~$ ls test�sktest.txt
test?sktest.txt
Find 揭示了转义序列:
robbie@phil:~$ find test�sktest.txt -ls
424512 4000 -rwxr--r-x 1 robbie robbie 4091743 Jan 26 00:34 test\323sktest.txt
我什至可以在控制台上输入他们的名字的唯一原因是制表符完成。这也意味着我可以手动重命名它们(并删除特殊字符)。
我已将 LC_ALL 设置为 UTF-8,这似乎没有帮助(在新 shell 上也没有帮助):
robbie@phil:~$ echo $LC_ALL
en_US.UTF-8
我正在使用 ssh 从我的 mac 连接到机器。这是 Ubuntu 安装:
robbie@phil:~$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=7.10
DISTRIB_CODENAME=gutsy
DISTRIB_DESCRIPTION="Ubuntu 7.10"
Shell 是 Bash,TERM 设置为 xterm-color。
这些文件已经存在很长一段时间了,而且它们并不是使用 Ubuntu 安装创建的。所以我不知道系统编码设置曾经是什么。
我已经尝试过以下方法:
find . -type f -ls | sed 's/[^a-zA-Z0-9]//g'
但我找不到一个可以完成我想要的一切的解决方案:
- 识别所有具有无法显示字符的文件(上面忽略了太多)
- 对于目录树中的所有文件(递归地),执行 mv oldname newname
- (可选)将特殊字符(例如 ä)音译为 a(不是必需的,但会很棒)
或者
- 正确显示所有这些文件(并且尝试打开它们时应用程序中没有错误)
我有一些零碎的东西,比如迭代所有文件并移动它们,但识别文件并为 mv 命令正确格式化它们似乎是困难的部分。
也欢迎任何有关为什么它们无法正确显示或如何“猜测”正确编码的额外信息。 (我尝试过 convmv 但它似乎并没有完全达到我想要的效果:http://j3e.de/linux/convmv/)
答案1
我猜您看到这个�
无效字符是因为该名称包含无效的 UTF-8 字节序列。典型的 unix 文件系统(包括您的文件系统)上的文件名是字节字符串,由应用程序决定使用什么编码。如今,使用 UTF-8 已成为一种趋势,但它并不通用,尤其是在无法使用纯 ASCII 的语言环境中,并且在 UTF-8 存在之前就一直在使用其他编码。
尝试LC_CTYPE=en_US.iso88591 ls
查看文件名在 ISO-8859-1 (latin-1) 中是否有意义。如果没有,请尝试其他区域设置。请注意,这里只有LC_CTYPE
区域设置很重要。
在 UTF-8 语言环境中,以下命令将显示名称不是有效 UTF-8 的所有文件:
grep-invalid-utf8 () {
perl -l -ne '/^([\000-\177]|[\300-\337][\200-\277]|[\340-\357][\200-\277]{2}|[\360-\367][\200-\277]{3}|[\370-\373][\200-\277]{4}|[\374-\375][\200-\277]{5})*$/ or print'
}
find | grep-invalid-utf8
您可以检查它们在其他语言环境中是否更有意义重新编码或者图标:
find | grep-invalid-utf8 | recode latin1..utf8
find | grep-invalid-utf8 | iconv -f latin1 -t utf8
一旦确定一堆文件名采用某种编码(例如 latin1),重命名它们的一种方法是
find | grep-invalid-utf8 |
rename 'BEGIN {binmode STDIN, ":encoding(latin1)"; use Encode;}
$_=encode("utf8", $_)'
这使用了 perl改名命令在 Debian 和 Ubuntu 上可用。您可以传递它-n
来显示它将执行的操作,而无需实际重命名文件。
答案2
我知道这是一个老问题,但我整晚都在寻找类似的解决方案。我发现了一些有用的提示,但它们并没有完全满足我的需要,因此我必须混合搭配一些提示才能获得我正在寻找的正确结果
只需删除特殊字符并将其替换为 (.) 点
for f in *.txt; do mv "$f" `echo $f | sed "s/[^a-zA-Z0-9.]/./g"`; done
为了在 cronjob 中使用,我做了以下每分钟运行一次的操作
*/1 * * * * cd /path/to/files/ && for f in *.txt; do mv "$f" `echo $f | sed "s/[^a-zA-Z0-9.]/./g"`; done >/dev/null 2>&1
我希望有人觉得这很有帮助,因为它让我很开心:)
答案3
现在,当您知道远程端的文件名使用哪种编码(“latin1”——根据第一个答案的注释)时,您还可以按照第二种方式-- 运行本地终端并SSH以这样的方式远程文件名正确显示(而不是第一种方式:重命名它们)。
喜欢我,您可以在本地启动一个以该特殊编码工作的终端,也许像这样:
LC_ALL=en_US.latin1 xvt &
xvt
代表你的终端程序。
也许,现有的语言环境被称为en_US.iso88591
,而不是en_US.latin1
我假设的那样。
答案4
这不满足批量要求,但我刚刚遇到了类似的问题,我有一个文件的多个版本,其名称相似,仅由一个奇怪的字符不同。不幸的是,这意味着我无法使用我通常使用的通配符技巧来重命名罪犯。
最后,我使用 Filezilla 作为 SFTP 客户端进行连接,浏览文件并使用 GUI 重命名它们。 Filezilla 很好地处理了狡猾的字符。