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

时间:2017-03-04 15:06:48

标签: linux bash shell command

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

2 个答案:

答案 0 :(得分:1)

您无法使用单个基本ls命令执行此操作。您必须使用其他POSIX / Unix / GNU实用程序的组合。例如,要首先找到重复的文件名:

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

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

bash

这意味着将文件while read filename; do find . -name "$filename" -print; done < dupes while的所有内容循环(dupes)到每行的变量read。对于每一行,再次执行filename并搜索find的特定-name并将其打印出来($filename,但它是隐含的,因此这是多余的)。

说实话,你可以在不使用中间文件的情况下将它们结合起来:

-print

如果您不熟悉它,find . -type f -exec basename "\{}" \; | sort | uniq -d | while read filename; do find . -name "$filename" -print; done 运算符意味着,使用上一个命令的输出作为以下命令的输入执行以下命令。例如:

|

免责声明:要求声明文件名均为数字。虽然我已经尝试设计代码来处理带空格的文件名(并且在我的系统上进行测试,但它有效),代码可能会在遇到特殊字符,换行符,nuls或其他异常情况时中断。请注意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 参数有特殊的安全注意事项,root用户不应该使用任意用户文件。提供的简化示例仅用于说明和说明目的。有关完整的安全问题,请参阅您的-exec页面和相关的CERT建议。

答案 1 :(得分:0)

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

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

无论文件名是否包含空格或新行等特殊字符(在极少数情况下),这都将确保结果正确无误。您可以构建一个数组,只需找到此数组中的重复文件,而不是双重运行查找。然后使用&#34; duplicates&#34; 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,"==>",$0}' |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/file$100
./tmp/tmp2/file14.txt.bak
./tmp/tmp2/file15.txt.bak
./tmp/file1
./tmp/file4
./file09 99
./file14
./file$100
./file1

$ dupes=$(LC_ALL=C sort <(printf '\<%s\>$\n' "${fn[@]##*/}") |uniq -d)
#Locate duplicate files
$ echo "$dupes"
\<file$100\>$ #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,"==>",$0}' |LC_ALL=C sort
file$100 ==> ./file$100          #File with special char correctly captured
file$100 ==> ./tmp/tmp2/file$100
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在排序时需要正确排序文件名。

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

    将查找结果转换为数组

    IFS = read -r -d&#39;&#39; res; do fn + =(&#34; $ res&#34;); done&lt; &lt;(find .... -print0)