文件描述符是否优化了对文件的写入?

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)

  • 当然,您也可以将整个循环重定向到 x: `for i in {1..1000}; 做回声 $i; 完成> x`。我会说这是最易读的版本。 (6认同)

Mat*_*lis 7

是的,效率更高

最简单的测试方法是将计数增加到 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)