如何只得到文件名与Linux '找到'?

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

364317 次浏览

在GNU find中,你可以使用-printf参数,例如:

find /dir1 -type f -printf "%f\n"

如果您正在使用GNU查找

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

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

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

我已经找到了一个解决方案(在makandracards页面),这给了最新的文件名:

ls -1tr * | tail -1

(感谢Arne Hartherz)

我将它用于cp:

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

如果你只想对文件名执行一些操作,使用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

使用-execdir自动保存{}中的当前文件,例如:

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

你也可以用$PWD代替.(在某些系统中,它不会在前面产生一个额外的点)。

如果你仍然有一个额外的点,或者你可以运行:

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

-execdir utility [argument ...] ;

-execdir主函数与-exec主函数是相同的,不同之处在于实用程序将从保存当前文件. exe目录中执行。

当使用+而不是;时,对于每次实用程序调用,{}将被尽可能多的路径名替换。换句话说,它将在一行中打印所有文件名。

在mac (BSD find)上使用:

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

-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的并行性也有帮助。

有趣的是,没有-n1,我无法解释xargs的最后一种情况。 它给出了正确的结果,并且它是最快的¯\_(ツ)_/¯

(basename只接受一个路径参数,但xargs将发送所有路径参数(实际上是5000),而不需要-n1。不适用于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

老实说,basenamedirname解决方案更简单,但你也可以看看这个:

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

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

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

正如其他人指出的那样,你可以结合findbasename,但默认情况下,basename程序一次只在一条路径上操作,所以可执行文件必须为每条路径启动一次(使用find ... -execfind ... | xargs -n 1),这可能会很慢。

如果你在basename上使用-a选项,那么它可以在一次调用中接受多个文件名,这意味着你可以在没有-n 1的情况下使用xargs,将路径组合在一起,形成更少的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实际上要快得多。