如何使用ls命令和选项列出不同目录中的重复文件名?
答案 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)