在大多数 Linux 文件系统上,NUL ( \0
) 是路径中唯一的无效字符(并被/
保留作为路径分隔符)。Windows 对于有效路径有一套复杂的规则。而不是自动修复路径(危险,可能会导致一个文件覆盖另一个文件),如何找到与 Windows 不兼容的目录中的所有路径?
最初的问题是我的谷歌云端硬盘文件夹位于使用安装的驱动器上ext2fs,但官方 Gdrive 客户端告诉我数千个文件无法同步。我找不到任何错误消息,当我要求它向我显示文件时,它只会无限期地挂起。重新启动客户端或操作系统没有帮助,但我有预感修复任何与 Windows 不兼容的路径会取消Gdrive。看来已经起作用了...
答案1
具有保留名称/字符的路径:
LC_ALL=C find . -name '*[[:cntrl:]<>:"\\|?*]*' \
-o -iname 'CON' \
-o -iname 'PRN' \
-o -iname 'AUX' \
-o -iname 'NUL' \
-o -iname 'COM[1-9]' \
-o -iname 'LPT[1-9]' \
-o -name '* ' \
-o -name '?*.'
测试:
$ cd -- "$(mktemp --directory)"
$ touch foo \\ LPT9 'space ' 'dot.'
$ LC_ALL=C find . -name '*[[:cntrl:]<>:"\\|?*]*' -o -iname 'CON' -o -iname 'PRN' -o -iname 'AUX' -o -iname 'NUL' -o -iname 'COM[1-9]' -o -iname 'LPT[1-9]' -o -name '* ' -o -name '?*.'
./dot.
./space
./LPT9
./\
忽略大小写时相同的路径 (不适用于包含换行符的路径):
find . | sort | LC_ALL=C tr '[:upper:]' '[:lower:]' | uniq -c | grep -v '^ 1 ' | cut -c '9-'
测试:
$ cd -- "$(mktemp --directory)"
$ touch foo bar BAR
$ find . | sort | LC_ALL=C tr '[:upper:]' '[:lower:]' | LC_ALL=C uniq -c | grep -v '^ 1 ' | cut -c '9-'
./bar
路径长度超过MAX_PATH
(260 个字符):
find "$PWD" | while IFS= read -r path
do
if [ "${#path}" -gt 260 ]
then
printf '%s\n' "$path"
fi
done
测试:
$ cd -- "$(mktemp --directory)"
$ touch foo 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345
$ find "$PWD" | while IFS= read -r path
> do
> if [ "${#path}" -gt 260 ]
> then
> printf '%s\n' "$path"
> fi
> done
/tmp/tmp.HEANyAI8Hy/123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345
答案2
对于 Perl 解决方案,您可以使用该Win32::Filenames
模块。
http://search.cpan.org/~bch/Win32-Filenames-0.01/lib/Win32/Filenames.pm
使用该模块的简单程序:
#!/usr/bin/env perl
use strict;
use warnings;
use Win32::Filenames qw(validate sanitize);
while ( my $filename = shift ) {
printf( "checking filename %s\t", $filename );
if ( validate($filename) ) {
print("ok.\n");
}
else {
printf( "failed, try this instead: %s\n", sanitize($filename) );
}
}
您可以在命令行上使用一个或多个文件名来调用它:
$ ./check.pl '<a>' 'hi?' '"right"' 'pipe|me'
checking filename <a> failed, try this instead: -a-
checking filename hi? failed, try this instead: hi-
checking filename "right" failed, try this instead: -right-
checking filename pipe|me failed, try this instead: pipe-me
对于整个文件层次结构:
$ find . -exec basename {} \; | xargs ./check.pl