使用重定向/管道时 bash 实际上如何更改 stdin/stdout/stderr

使用重定向/管道时 bash 实际上如何更改 stdin/stdout/stderr

不幸的是,我没有运气弄清楚这一点,因为我发现的所有内容都只是重定向的语法,或者有关重定向如何工作的浅层信息。

我想知道的是当您使用管道或重定向时bash实际上如何stdin改变。例如,如果您执行:stdoutstderr

ls -la > diroutput.log

它如何改变stdoutlsdiroutput.log

我认为它的工作原理是这样的:

  • Bash 运行fork(2)以创建自身的副本
  • 分叉的 bash 进程将其设置stdoutdiroutput.log使用类似的东西freopen(3)
  • 分叉的 bash 进程运行execve(2)或类似的 exec 函数来替换自身,ls现在使用stdoutbash 的设置

但这只是我有根据的猜测。

答案1

我能够使用strace -fC 语言并编写一个小的概念证明来解决这个问题。

看来 bash 只是在调用之前操作子进程中的文件描述符,execve正如我所想的那样。

工作原理如下ls -la > diroutput.log(大致):

  • bash 调用fork(2)
  • 分叉的 bash 进程看到输出重定向并diroutput.log使用open(2).
  • 分叉的 bash 进程stdout使用dup2(2)系统调用替换文件描述符
  • bash 调用execve(2)替换它的可执行映像,ls然后继承已经设置的stdout

相关的系统调用如下所示(strace输出):

6924  open("diroutput.log", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3 
6924  dup2(3, 1)                        = 1 
6924  close(3)                          = 0 
6924  execve("/bin/ls", ["ls", "-la"], [/* 77 vars */]) = 0

相关内容