我正在尝试将目录中所有 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
为什么 echo 比 touch 快这么多?
答案1
在 bash 中,touch
是外部二进制文件,但是echo
是外壳内置:
$ type echo
echo is a shell builtin
$ type touch
touch is /usr/bin/touch
由于touch
是外部二进制文件,并且每个文件调用touch
一次,因此 shell 必须创建 300,000 个 实例touch
,这需要很长时间。
echo
然而,它是一个 shell 内置函数,并且 shell 内置函数的执行根本不需要 fork。相反,当前 shell 会执行所有操作,并且不会创建任何外部进程;这就是它速度如此之快的原因。
以下是 shell 操作的两个概况。您可以看到,使用时花费了大量时间来克隆新进程touch
。使用/bin/echo
而不是内置的 shell 应该会显示出更具可比性的结果。
使用触摸
$ 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
[...]
使用回声
$ 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
[...]
答案2
正如其他人所回答的那样,使用命令比通常(尽管不要求)内置于 shell 中的命令echo
更快。使用它可以免除与为您获得的每个文件运行启动新进程相关的内核开销。touch
echo
touch
但是,请注意,实现此效果的最快方法仍然是使用touch
,但不是为每个文件运行一次程序,而是可以使用选项-exec
withfind
来确保仅运行几次。这种方法通常会更快,因为它避免了与 shell 循环相关的开销:
find . -name "*.xml" -exec touch {} +
如果可能的话,使用+
(而不是\;
)withfind ... -exec
仅运行该命令一次,并将每个文件作为参数。如果参数列表非常长(如 300,000 个文件的情况),将使用长度接近限制的参数列表进行多次运行(ARG_MAX
在大多数系统上)。
这种方法的另一个优点是,它对于包含所有空白字符的文件名表现稳健,而原始循环的情况则不同。
答案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
如上所述,使用公用事业与内置导致性能显着下降。以下是使用创建约 9000 个文件所需时间的统计数据内置 echo
和公用事业 echo
:
# 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