如何按名称对 Linux 服务器中的文件进行分类?

How do I classify files in Linux server by their names?

如何使用 ls 命令和选项列出不同目录中的重复文件名?

您不能使用单一的基本 ls 命令来完成此操作。您必须结合使用其他 POSIX/Unix/GNU 实用程序。例如,首先查找重复的文件名:

find . -type f -exec basename "\{}" \; | sort | uniq -d > dupes

这意味着find当前目录(.)中整个目录层次结构中的所有文件(-type f),并执行(-exec)命令basename(去除目录部分)找到的文件 (\{}),命令结束 (\;)。然后对这些文件进行排序并打印出重复行 (uniq -d)。结果进入文件 dupes。现在您有了重复的文件名,但您不知道它们在哪个目录中。再次使用 find 找到它们。使用 bash 作为你的 shell:

while read filename; do find . -name "$filename" -print; done < dupes

这意味着循环遍历 (while) 文件 dupesread 的所有内容到变量 filename 的每一行。对于每一行,再次执行 find 并搜索 $filename 的特定 -name 并将其打印出来(-print,但它是隐含的,所以这是多余的)。

说实话,您可以在不使用中间文件的情况下组合这些文件:

find . -type f -exec basename "\{}" \; | sort | uniq -d | while read filename; do find . -name "$filename" -print; done

如果您不熟悉它,| 运算符的意思是,使用上一个命令的输出作为下一个命令的输入来执行下一个命令。示例:

eje@EEWANCO-PC:~$ mkdir test
eje@EEWANCO-PC:~$ cd test
eje@EEWANCO-PC:~/test$ mkdir 1 2 3 4 5
eje@EEWANCO-PC:~/test$ mkdir 1/2 2/3
eje@EEWANCO-PC:~/test$ touch 1/0000 2/1111 3/2222 4/2222 5/0000 1/2/1111 2/3/4444
eje@EEWANCO-PC:~/test$ find . -type f -exec basename "\{}" \; | sort | uniq -d | while read filename; do find . -name "$filename" -print; done
./1/0000
./5/0000
./1/2/1111
./2/1111
./3/2222
./4/2222

免责声明:要求声明文件名全部为数字。虽然我尝试设计代码来处理带空格的文件名(并且在我的系统上进行的测试,它可以工作),但当遇到特殊字符、换行符、空值或其他异常情况时,代码可能会中断。请注意 -exec 参数有特殊的安全考虑,root 用户不应将其用于任意用户文件。提供的简化示例仅用于说明和教学目的。请查阅您的 man 页面和相关的 CERT 建议以了解完整的安全隐患。

我的 bash 配置文件 (bash 4.4) 中有一个用于重复文件的功能。 的确,find 是正确的工具。

我将查找与 -print0 选项结合使用,它使用空字符而不是新行分隔查找结果(默认查找操作)。现在我可以捕获当前目录和子目录下的所有文件。

这将确保无论文件名是否包含空格或换行符等特殊字符(在极少数情况下),结果都是正确的。您可以构建一个数组,然后只在该数组中定位重复的文件,而不是对查找进行双重 运行 查找。然后使用 "duplicates" 作为模式对整个数组进行 grep。

所以像这样的东西对我的功能没问题:

$ IFS= readarray -t -d '' fn< <(find . -name 'file*' -print0)
$ dupes=$(LC_ALL=C sort <(printf '\<%s\>$\n' "${fn[@]##*/}") |uniq -d)
$ grep -e "$dupes" <(printf '%s\n' "${fn[@]}")  |awk -F/ '{print $NF,"==>",[=10=]}' |LC_ALL=C sort

这是一个测试:

$ IFS= readarray -t -d '' fn< <(find . -name 'file*' -print0)
# find all files and load them in an array using null delimiter
$ printf '%s\n' "${fn[@]}" #print the array
./tmp/file7
./tmp/file14
./tmp/file11
./tmp/file8
./tmp/file9
./tmp/tmp2/file09 99
./tmp/tmp2/file14.txt
./tmp/tmp2/file15.txt
./tmp/tmp2/file0
./tmp/tmp2/file14.txt.bak
./tmp/tmp2/file15.txt.bak
./tmp/file1
./tmp/file4
./file09 99
./file14
./file0
./file1

$ dupes=$(LC_ALL=C sort <(printf '\<%s\>$\n' "${fn[@]##*/}") |uniq -d)
#Locate duplicate files
$ echo "$dupes"
\<file0\>$ #Mind this one with special char $ in filename
\<file09 99\>$ #Mind also this one with spaces
\<file14\>$
\<file1\>$
#I have on purpose enclose the results between \<...\> to force grep later to capture full words and avoid file1 to match file1.txt or file11

$ grep -e "$dupes" <(printf '%s\n' "${fn[@]}")  |awk -F/ '{print $NF,"==>",[=11=]}' |LC_ALL=C sort
file0 ==> ./file0          #File with special char correctly captured
file0 ==> ./tmp/tmp2/file0
file09 99 ==> ./file09 99        #File with spaces in name also correctly captured
file09 99 ==> ./tmp/tmp2/file09 99
file1 ==> ./file1
file1 ==> ./tmp/file1
file14 ==> ./file14              #other files named file14 like file14.txt and file14.txt.bak not captured since they are not duplicates.
file14 ==> ./tmp/file14

提示:

  • 这个 <(printf '\<%s\>$\n' "${fn[@]##*/}") 使用 bash 内置参数扩展技术在查找结果的基名上使用进程替换。

  • LC_ALL=按文件名正确排序的顺序需要 C。

  • 在 bash 4.4 之前的版本中,readarray 不接受 -d 选项(分隔符)。在这种情况下,您可以使用

    将查找结果转换为数组

    while IFS= read -r -d '' res;do fn+=( "$res" );done < <(find.... -print0)