Bash:文件描述符

15

我是一名Bash初学者,但我正在努力学习这个工具,以便将来在计算机领域找到一份工作。

现在我正在尝试自学文件描述符。让我分享一些我的实验:


#!/bin/bash

# Some dummy multi-line content
read -d '' colours <<- 'EOF'
        red
        green
        blue
EOF

# File descriptor 3 produces colours
exec 3< <(echo "$colours")

# File descriptor 4 filters colours
exec 4> >(grep --color=never green)

# File descriptor 5 is an unlimited supply of violet
exec 5< <(yes violet)

echo Reading colours from file descriptor 3...
cat <&3
echo ... done.

echo Reading colours from file descriptor 3 again...
cat <&3
echo ... done.

echo Filtering colours through file descriptor 4...
echo "$colours" >&4
echo ... done. # Race condition?

echo Dipping into some violet...
head <&5
echo ... done.

echo Dipping into some more violet...
head <&5
echo ... done.

看到上面的输出,我的脑海中浮现出一些问题:

  1. fd3 在“使用”后似乎会被“耗尽”,那么它是否在第一次使用后自动关闭?
  2. fd3 和命名管道有什么不同?(我已经查过一些资料)
  3. yes 命令究竟是在何时开始执行的?在 fd 声明时还是之后?
  4. yes 会在需要更多输入时停止(CTRL-Z 或其他),然后重新启动吗?
  5. 我如何获取 yes 的 PID?
  6. 我能否获取“活动”fd的列表?
  7. 过滤通过 fd4 的竞争条件非常有趣,能否避免这种情况发生?
  8. yes 只有在我使用 exec 5>&- 命令后才会停止吗?
  9. 使用 >&- 关闭与使用 <&- 是否有关系?

暂时就问到这里,谢谢!

PS:部分(编号)回答没问题...我会自己整理。 (虽然一个人给出全面的答案会很印象深刻!)

1个回答

18

fd3似乎在“使用”后会被“耗尽”,第一次使用后它是否也会自动关闭?

不会,它不会关闭。这是由于 exec 的工作方式。在您使用无参数的 exec 模式时,其功能是按照自己指定的 I/O 重定向请求来安排 shell 自己的文件描述符,并且保持它们的状态直到脚本终止或稍后再次更改。

稍后,cat 在其标准输入上接收此文件描述符 3 的副本(文件描述符 0)。当 cat 退出时(或者甚至可能在 cat 退出之前关闭它,但这并不重要),cat 的标准输入会自动关闭。原始文件的副本仍然存在,即 shell 的文件描述符 3。虽然实际文件已达到 EOF,不会再从中读取任何内容。

fd3与命名管道有何不同?(我已经看过了一些)

Shell 的 <(某个命令) 语法(不是标准 Bourne Shell 语法,我认为仅在 zshbash 中可用),实际上可能是使用命名管道实现的。在 Linux 上可能不是这样,因为有更好的方法(使用 /dev/fd),但在其他操作系统上可能是这样。

因此,在某种意义上,此语法可能或可能不是设置命名管道的帮助程序。

yes 命令何时开始执行?在 fd 声明时还是稍后?

只要求值 <(yes violet) 结构(发生在求值 exec 5< <(yes violet) 时)。

yes 是否会停止(CTRL-Z 或其他),并在需要更多 violet 时重新启动?

不,它不会停止。但是,当它开始输出比管道另一端正在读取的任何内容都多时,它很快就会被阻塞。换句话说,管道缓冲区将变满。

如何获取yes的PID?

好问题!在执行yes之后,$!立即包含它。但是似乎有一个中间子shell,实际上你得到的是那个子shell的pid。尝试<(exec yes violet)来避免中间过程。

我能获得“活动”fd列表吗?

不能从shell中获得。但如果您使用像Linux这样具有/proc的操作系统,则可以直接查看/proc/self/fd

通过fd4筛选的非常有趣的竞态条件,能否避免它?

要避免它,您可能想要在继续执行脚本之前等待grep进程完成。如果您获取了该进程的进程ID(如上所述),我认为您应该能够wait它。

只有当我使用5>&-时,yes才会停止吗?

是的。然后将发生的是,yes将继续尝试永远产生输出,但当文件描述符的另一端关闭时,它将获得写入错误(EPIPE)或默认情况下是致命的信号(SIGPIPE)。

使用>&-还是<&-是否重要?

不重要。两种语法都可用以保持一致性。


  1. 它被 cat 关闭了,啊哈!
  2. 收到!
  3. 这好像从未“阻塞等待”? exec 3< <(while true; do date '+%H-%M-%S' | tee -a /tmp/log; sleep 1; done); tail -f /tmp/log
  4. 羞愧!:(
  5. 你教我了 wait!! sleep 5 & pid=$!; echo before; wait $pid; echo after - 太棒了!令人印象深刻的答案!!谢谢!!
- Robottinosino
1
回复:“这似乎从不阻塞和等待”。当内核决定在管道中缓冲了足够的数据时,它最终会被阻塞……我更改了您的示例,以便每次循环都输出一堆垃圾文本(只是为了加快速度),对我来说,当/tmp/log文件达到约48千字节时,它就被阻塞了。仅使用“date '+%H-%M-%S'”仍然会发生,只是需要更长时间 :-) - Celada
@Robottinosino 从一张照片很难确定一个人的性别,但是名字结合起来看的话,“Celada”或许是个女性。如果是的话,我会选择“你就是女主角☆”,不过我也不确定这样会有什么反应。 ;) - Joe
@Joe 谢谢纠正 :-) 不过为了对 Robottinosino 公平,我想当时我可能没有在我的个人资料上放图片或外部链接,所以很难判断。 - Celada

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接