如何仅使用Linux的“find”命令获取文件名?

364

我正在使用find命令查找目录中的所有文件,以获取路径列表。但是,我只需要文件名,例如,我得到了./dir1/dir2/file.txt,我想要得到file.txt

10个回答

493
在GNU find中,你可以使用-printf参数来实现这一点,例如:
find /dir1 -type f -printf "%f\n"

15
答案很清楚,但缺乏细节。 - Jason McCreary
3
当我使用多个文件类型(-o开关)时,这对我不起作用。 - Urchin
20
找到:-printf: 未知的主要条件或运算符。 - holms
找到 ./ 目录下所有文件名,然后使用 xargs 命令搜索 "searchName"。但是这个命令不起作用,它只会打印出以下信息:about.php: No such file or directory grep: site-themes.php: No such file or directory。或者尝试使用 find ./ -name "*" | xargs grep "searchName" -printf "%f\n" 仍然会出现错误。 - Gank
2
我认为这不是正确的答案。find -printf "%f\n" -exec echo {} \; 显示原始文件名被传递了下去。 - Alex Riina
显示剩余6条评论

214

如果你的find命令没有-printf选项,你也可以使用basename:

find ./dir1 -type f -exec basename {} \;

22
引用分号是消除歧义的另一种方式:... {} ';' - davidchambers
exec 调用与上面的 printf 解决方案相比会产生显著的性能开销。 - ABC Taylor

39

使用-execdir,它会自动将当前文件保存在{}中,例如:

find . -type f -execdir echo '{}' ';'

在某些系统中,您还可以使用$PWD代替.(这样不会在前面产生多余的点)。

如果您仍然得到了额外的点,则可以尝试运行以下命令:

find . -type f -execdir basename '{}' ';'

-execdir utility [argument ...] ;

-execdir 命令与 -exec 命令十分相似,但是命令 utility 会在包含当前文件的目录下执行。

如果使用 + 替代 ;,那么对于每次调用 utility,{} 将被尽可能多的路径名替换。换句话说,它将在一行中打印所有文件名。


1
我得到的是 ./filename 而不是 filename。根据您的需求,这可能是可以接受的,也可能不行。 - user276648
1
@user276648 请尝试使用 $PWD 替代 . - kenorb

36

如果您正在使用GNU find命令

find . -type f -printf "%f\n"

您也可以使用编程语言,如 Ruby(1.9+)。

$ ruby -e 'Dir["**/*"].each{|x| puts File.basename(x)}'

如果你喜欢使用Bash(至少4个)的解决方案

shopt -s globstar
for file in **; do echo ${file##*/}; done

我受到你的回答的启发,想要帮助sleske:http://serverfault.com/a/745968/329412 - Nolwennig

12

如果您只想对文件名执行某些操作,使用basename可能会很困难。

例如:

find ~/clang+llvm-3.3/bin/ -type f -exec echo basename {} \; 

这将只是回显basename/my/found/path。如果我们想要在文件名上执行操作,则不是我们想要的。

但您可以使用xargs输出。例如,根据另一个目录中的名称杀死目录中的文件:

cd dirIwantToRMin;
find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \; | xargs rm

1
请勿回显 - find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \; - commonpike

8

在Mac上(BSD find),请使用以下命令:

find /dir1 -type f -exec basename {} \;

7
正如其他人所指出的,你可以结合使用find和basename命令,但是默认情况下basename程序一次只能处理一个路径,因此对于每个路径都需要启动一次可执行文件(使用find ... -exec或 find ... | xargs -n 1),这可能会潜在地变慢。
如果您在basename上使用-a选项,则它可以在单个调用中接受多个文件名,这意味着您可以使用xargs而无需-n 1将路径组合成更少量的basename调用,这应该更有效率。
例如:
find /dir1 -type f -print0 | xargs -0 basename -a

我这里包含了-print0-0(需要一起使用),以处理文件和目录名称中的任何空格。

下面是xargs basename -axargs -n1 basename两个版本的时间比较。(为了进行类似与类似的比较,这里报告的时间都是在初始虚拟运行后进行的,因此它们都是在文件元数据已经被复制到I/O缓存后完成的。)在这两种情况下,我都将输出传递到cksum,只是为了证明输出独立于所使用的方法。

$ time sh -c 'find /usr/lib -type f -print0 | xargs -0 basename -a | cksum'
2532163462 546663

real    0m0.063s
user    0m0.058s
sys 0m0.040s

$ time sh -c 'find /usr/lib -type f -print0 | xargs -0 -n 1 basename | cksum' 
2532163462 546663

real    0m14.504s
user    0m12.474s
sys 0m3.109s

正如你所看到的,避免每次启动 basename 确实可以显著提高速度。


仔细阅读@minusf的答案后,我发现在Mac上basename将接受多个文件名而不需要任何额外的命令行参数。这里使用的是Linux中的-a。(basename --version告诉我basename (GNU coreutils) 8.28。) - alani

4

说实话,basenamedirname的解决方案更简单易懂,但你也可以尝试以下方法:

find . -type f | grep -oP "[^/]*$"

或者

find . -type f | rev | cut -d '/' -f1 | rev

或者

find . -type f | sed "s/.*\///"

1
“basename” 的解决方案对我没用,但将结果通过管道传递到 “sed” 中却非常有效,感谢您的帮助。 - dschib

3

-exec-execdir执行速度较慢,xargs是首选。

$ alias f='time find /Applications -name "*.app" -type d -maxdepth 5'; \
f -exec basename {} \; | wc -l; \
f -execdir echo {} \; | wc -l; \
f -print0 | xargs -0 -n1 basename | wc -l; \
f -print0 | xargs -0 -n1 -P 8 basename | wc -l; \
f -print0 | xargs -0 basename | wc -l

     139
    0m01.17s real     0m00.20s user     0m00.93s system
     139
    0m01.16s real     0m00.20s user     0m00.92s system
     139
    0m01.05s real     0m00.17s user     0m00.85s system
     139
    0m00.93s real     0m00.17s user     0m00.85s system
     139
    0m00.88s real     0m00.12s user     0m00.75s system

xargs 的并行性也有所帮助。

有趣的是,我无法解释 xargs 的最后一个案例,如果没有 -n1。 它提供了正确的结果,并且是最快的 ¯\_(ツ)_/¯

basename 只需要一个路径参数,但是如果没有 -n1xargs 将发送所有路径参数(实际上是 5000 个)。 在 linux 和 openbsd 上无法工作,只能在 macOS 上使用...)

从 Linux 系统中获取一些更大的数字,以查看 -execdir 如何帮助,但仍然比并行的 xargs 慢得多:

$ alias f='time find /usr/ -maxdepth 5 -type d'
$ f -exec basename {} \; | wc -l; \
f -execdir echo {} \; | wc -l; \
f -print0 | xargs -0 -n1 basename | wc -l; \
f -print0 | xargs -0 -n1 -P 8 basename | wc -l

2358
    3.63s real     0.10s user     0.41s system
2358
    1.53s real     0.05s user     0.31s system
2358
    1.30s real     0.03s user     0.21s system
2358
    0.41s real     0.03s user     0.25s system

2
只需要再提供一个数据点:在OpenBSD上,长时间运行的find中,-execdir成为最快的选项,因为创建新进程是一项相对昂贵的操作。 - minusf

-2

我找到了一个解决方案(在makandracards页面上),它只提供最新的文件名:

ls -1tr * | tail -1

感谢Arne Hartherz的贡献

我在使用cp时用到了它:

cp $(ls -1tr * | tail -1) /tmp/

2
这完全没有回答问题。 - kenorb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接