如何按名称对 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
) 文件 dupes
和 read
的所有内容到变量 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)
如何使用 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
) 文件 dupes
和 read
的所有内容到变量 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)