我正在尝试(递归地)将目录中所有xml文件的时间戳更新为当前时间。我正在使用Mac OSX 10.8.5。

在大约300,000个文件上,以下echo命令需要10秒钟:

for file in `find . -name "*.xml"`; do echo >> $file; done


但是以下touch命令需要10分钟! :

for file in `find . -name "*.xml"`; do touch $file; done


为什么回声比触摸此处要快得多?

评论

附带一提:您知道这两个命令并不等效,不是吗?至少对于Unix / Linux,echo >> $ file将在$ file后面添加换行符,然后对其进行修改。我认为对于OS / X它将是相同的。如果您不想这样做,请使用echo -n >> $ file。

也不会碰`find。名称“ * .xml”`甚至比上述两者都快?

或者只考虑>> $ file

这不是对明确问题的答案,但是为什么要调用触摸那么多次呢?找 。名称'* .xml'-print0 | xargs -0 touch调用触摸的次数要少得多(可能只有一次)。在Linux上可以使用,在OS X上也可以使用。

@elmo参数列表太长(很容易包含300.000个文件...)

#1 楼

在bash中,touch是一个外部二进制文件,而echo是一个内置的shell:

$ type echo
echo is a shell builtin
$ type touch
touch is /usr/bin/touch


由于touch是一个外部二进制文件,您对每个文件调用一次touch,因此该shell必须创建300,000个touch实例,这需要很长的时间。

echo是Shell内置的,执行shell内置不需要分叉。相反,当前的shell会执行所有操作,并且不会创建任何外部进程。这就是为什么它要快得多的原因。
这是Shell操作的两个配置文件。您会看到使用touch克隆新进程时花费了大量时间。使用/bin/echo代替内置的shell应该显示出更多可比的结果。


使用touch

$ strace -c -- bash -c 'for file in a{1..10000}; do touch "$file"; done'
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 56.20    0.030925           2     20000     10000 wait4
 38.12    0.020972           2     10000           clone
  4.67    0.002569           0     80006           rt_sigprocmask
  0.71    0.000388           0     20008           rt_sigaction
  0.27    0.000150           0     10000           rt_sigreturn
[...]



使用echo

$ strace -c -- bash -c 'for file in b{1..10000}; do echo >> "$file"; done'
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 34.32    0.000685           0     50000           fcntl
 22.14    0.000442           0     10000           write
 19.59    0.000391           0     10011           open
 14.58    0.000291           0     20000           dup2
  8.37    0.000167           0     20013           close
[...]


评论


您是在OS X上编译strace还是在另一个OS上运行测试?

–bmike
2014年4月9日19:49

@bmike我的测试是在Linux上进行的,但是原理是相同的。

–克里斯唐(Chris Down)
2014年4月10日在2:24

我完全同意-看到我对主要问题的评论,即/ bin / echo与/ bin / touch一样慢,因此推理是合理的。我只是想重现strace的时序,但使用dtruss / dtrace失败了,bash -c语法在OS X上也无法正常工作。

–bmike
2014年4月10日在2:56

#2 楼

正如其他人回答的那样,使用echo将比touch更快,因为echo是通常内置于外壳中的命令(尽管不是必需的)。使用它消除了与为touch获得的每个文件运行一个新进程有关的内核开销。

但是,请注意,达到此效果的最快方法仍然是使用touch,但是与为每个文件运行一次程序相比,可以将-exec选项与find一起使用,以确保仅运行几次。此方法通常会更快,因为它避免了与Shell循环相关的开销:

find . -name "*.xml" -exec touch {} +


+(与\;相对)与find ... -exec一起使用仅运行一次命令如果可能,将每个文件作为参数。如果参数列表很长(例如300,000个文件),那么将使用长度接近限制的参数列表进行多次运行(在大多数系统上为ARG_MAX)。

另一个优点这种方法的优点是,它可以对包含所有空格字符的文件名表现出强大的性能,而原始循环则不是如此。

评论


+1指出find +参数。我认为很多人都没有意识到这一点(我不是)。

– Gerrit
2014年4月9日12:50

并非所有版本的find都带有+参数。通过管道传递给xargs,可以获得类似的效果。

– Barmar
2014年4月9日在18:08

@ Barmar,POSIX要求+部分,因此应该是可移植的。 -print0不是。

– Graeme
2014年4月9日在21:01

我仍然偶尔会遇到没有它的实现。 YMMV。

– Barmar
2014年4月9日在21:03

@ChrisDown,我发现的发现是Busybox查找具有可用选项,但只将其视为;即可。在表面之下。

– Graeme
2014年4月10日在9:25

#3 楼

echo是内置的shell。另一方面,touch是一个外部二进制文件。

$ type echo
echo is a shell builtin
$ type touch
touch is hashed (/usr/bin/touch)


Shell内置程序要快得多,因为在加载程序时不会涉及开销,即没有fork / exec参与其中。这样,您会在执行内置命令与外部命令时观察到明显的时间差异。

这就是为什么像time这样的实用程序可用作shell内置程序的原因。 >
您可以通过说出以下内容来获取完整的shell内置列表:

enable -p



如上所述,使用实用程序而非内置函数导致性能显着下降。以下是使用内置echo和实用程序echo创建〜9000个文件所花费时间的统计信息:

# Using builtin
$ time bash -c 'for i in {1000..9999}; do echo > $i; done'

real    0m0.283s
user    0m0.100s
sys 0m0.184s

# Using utility /bin/echo
$ time bash -c 'for i in {1000..9999}; do /bin/echo > $i; done'

real    0m8.683s
user    0m0.360s
sys 0m1.428s


评论


而且我认为大多数系统上都有一个回显二进制文件(对我来说是/ bin / echo),因此您可以使用它而不是内置的方法重试时序测试。

– Michael Mrozek
2014年4月9日下午5:31

@MichaelMrozek添加了针对内置和二进制文件的计时测试。

– devnull
2014年4月9日下午13:06