7 shell bash io-redirection file-descriptors optimization
是否等同于将命令直接打印到文件,而不是写入文件描述符?
插图
直接写入文件:
for i in {1..1000}; do >>x echo "$i"; done
Run Code Online (Sandbox Code Playgroud)
使用 fd:
exec 3>&1 1>x
for i in {1..1000}; do echo "$i"; done
exec 1>&3 3>&-
Run Code Online (Sandbox Code Playgroud)
后一种效率更高吗?
ilk*_*chu 12
在循环之前打开文件exec
与将重定向放在循环中的命令中的主要区别在于,前者只需要设置一次文件描述符,而后者在循环的每次迭代中打开和关闭文件。
执行一次可能会更有效,但如果您要在循环内运行外部命令,则差异可能会在启动命令的成本上消失。(echo
这里可能是内置的,所以不适用)
如果输出将发送到常规文件以外的其他内容(例如,如果x
是命名管道),则打开和关闭文件的行为可能对其他进程可见,因此行为也可能存在差异。
请注意,重定向通过exec
和命令重定向之间实际上没有区别,它们都打开文件并处理文件描述符编号。
这两个应该非常等效,因为它们既是open()
文件又是文件write()
。(不过,fd 1 在命令期间的存储方式有所不同。):
for i in {1..1000}; do
>>x echo "$i"
done
for i in {1..1000}; do
exec 3>&1 1>>x # assuming fd 3 is available
echo "$i" # here, fd 3 is visible to the command
exec 1>&3 3>&-
done
Run Code Online (Sandbox Code Playgroud)
是的,效率更高
最简单的测试方法是将计数增加到 500000 并计时:
> time bash s1.sh; time bash s2.sh
bash s1.sh 16,47s user 10,00s system 99% cpu 26,537 total
bash s2.sh 10,51s user 3,50s system 99% cpu 14,008 total
Run Code Online (Sandbox Code Playgroud)
strace(1) 揭示了原因(我们有一个简单的write
,而不是open
+5* fcntl
+2* dup
+2* close
+ write
):
因为for i in {1..1000}; do >>x echo "$i"; done
我们得到:
open("x", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
write(1, "997\n", 4) = 4
dup2(10, 1) = 1
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
open("x", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
write(1, "998\n", 4) = 4
dup2(10, 1) = 1
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
open("x", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
write(1, "999\n", 4) = 4
dup2(10, 1) = 1
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
open("x", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
write(1, "1000\n", 5) = 5
dup2(10, 1) = 1
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
Run Code Online (Sandbox Code Playgroud)
而因为exec 3>&1 1>x
我们变得更干净
write(1, "995\n", 4) = 4
write(1, "996\n", 4) = 4
write(1, "997\n", 4) = 4
write(1, "998\n", 4) = 4
write(1, "999\n", 4) = 4
write(1, "1000\n", 5) = 5
Run Code Online (Sandbox Code Playgroud)
但请注意,差异不是因为“使用 FD”,而是因为您进行重定向的位置。例如,如果您要这样做,for i in {1..1000}; do echo "$i"; done > x
您将获得与第二个示例几乎相同的性能:
bash s3.sh 10,35s user 3,70s system 100% cpu 14,042 total
Run Code Online (Sandbox Code Playgroud)