文件描述符如何工作?
有人能告诉我为什么这不起作用吗?我正在摆弄文件描述符,但感觉有点失落。
#!/bin/bash
echo "This"
echo "is" >&2
echo "a" >&3
echo "test." >&4
前三行运行良好,但最后两行出错。为什么?
Can someone tell me why this does not work? I'm playing around with file descriptors, but feel a little lost.
#!/bin/bash
echo "This"
echo "is" >&2
echo "a" >&3
echo "test." >&4
The first three lines run fine, but the last two error out. Why?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
文件描述符 0、1 和 2 分别用于 stdin、stdout 和 stderr。
文件描述符 3、4、.. 9 用于附加文件。为了使用它们,您需要先打开它们。例如:
有关更多信息,请查看高级 Bash 脚本指南:第 20 章 I/O 重定向< /a>.
File descriptors 0, 1 and 2 are for stdin, stdout and stderr respectively.
File descriptors 3, 4, .. 9 are for additional files. In order to use them, you need to open them first. For example:
For more information take a look at Advanced Bash-Scripting Guide: Chapter 20. I/O Redirection.
这是一个老问题,但有一件事需要澄清。
虽然 Carl Norum 和 Dogbane 的答案是正确的,但假设是更改脚本以使其正常工作。
我想指出的是,您不需要更改脚本:
如果您以不同的方式调用它,它就会起作用:
这意味着将文件描述符 3 和 4 重定向到 1(这是标准的)输出)。
要点是,脚本完全可以写入除 1 和 2 之外的描述符(stdout 和 stderr)如果这些描述符是由父进程提供的。
您的示例实际上非常有趣,因为该脚本可以写入 4 个不同的文件:
现在您的输出位于 4 个单独的文件中:
更有趣的是您的程序不必写入这些文件的权限,因为它实际上并没有打开它们。
例如,当我运行 sudo -s 将用户更改为 root 时,以 root 身份创建一个目录,并尝试以普通用户(在我的情况下为 rsp)身份运行以下命令,如下所示
:错误:
但是如果我在
su
之外进行重定向:(注意单引号中的区别)它有效并且我得到
: root 拥有的目录中有 4 个 root 拥有的文件 - 即使该脚本没有创建这些文件的权限。
另一个例子是使用 chroot 监狱或容器并在其中运行一个程序,即使它以 root 身份运行,它也无法访问这些文件,并且仍然将这些描述符重定向到您需要的外部位置,而不实际授予对整个文件的访问权限系统或此脚本的其他任何内容。
重点是你发现了一个非常有趣且有用的机制。您不必像其他答案中所建议的那样打开脚本内的所有文件。有时在脚本调用期间重定向它们很有用。
总结一下,这:
实际上相当于:
运行程序为:
与:
数字 1 只是一个默认数字,它是 stdout。
但即使是这个程序:
也会产生“错误描述符”错误。如何?运行时:
输出将是:
添加
>&-
(与1>&-
相同)意味着关闭标准输出。添加2>&-
意味着关闭 stderr。您甚至可以做更复杂的事情。 原始脚本:
仅运行时:
:
But you can make描
的
您
当
prints ?难道没有什么失败的事情吗? 确实如此,但没有 stderr(文件描述符编号 2)你没有看到错误消息!
我认为通过这种方式进行实验以了解如何进行是非常有用的描述符及其重定向工作。
你的脚本确实是一个非常有趣的例子 - 我认为它根本没有损坏,你只是用错了它!:)
It's an old question but one thing needs clarification.
While the answers by Carl Norum and dogbane are correct, the assumption is to change your script to make it work.
What I'd like to point out is that you don't need to change the script:
It works if you invoke it differently:
which means to redirect file descriptors 3 and 4 to 1 (which is standard output).
The point is that the script is perfectly fine in wanting to write to descriptors other than just 1 and 2 (stdout and stderr) if those descriptors are provided by the parent process.
Your example is actually quite interesting because this script can write to 4 different files:
Now you have the output in 4 separate files:
What is more interesting about it is that your program doesn't have to have write permissions for those files, because it doesn't actually open them.
For example, when I run
sudo -s
to change user to root, create a directory as root, and try to run the following command as my regular user (rsp in my case) like this:I get an error:
But if I do the redirection outside of
su
:(note the difference in single quotes) it works and I get:
which are 4 files owned by root in a directory owned by root - even though the script didn't have permissions to create those files.
Another example would be using chroot jail or a container and run a program inside where it wouldn't have access to those files even if it was run as root and still redirect those descriptors externally where you need, without actually giving access to the entire file system or anything else to this script.
The point is that you have discovered a very interesting and useful mechanism. You don't have to open all the files inside of your script as was suggested in other answers. Sometimes it is useful to redirect them during the script invocation.
To sum it up, this:
is actually equivalent to:
and running the program as:
is the same as:
The number 1 is just a default number and it is stdout.
But even this program:
can produce a "Bad descriptor" error. How? When run as:
The output will be:
Adding
>&-
(which is the same as1>&-
) means closing the standard output. Adding2>&-
would mean closing the stderr.You can even do a more complicated thing. Your original script:
when run with just:
prints:
But you can make descriptors 3 and 4 work, but number 1 fail by running:
It outputs:
If you want descriptors both 1 and 2 fail, run it like this:
You get:
Why? Didn't anything fail? It did but with no stderr (file descriptor number 2) you didn't see the error messages!
I think it's very useful to experiment this way to get a feeling of how the descriptors and their redirection work.
Your script is a very interesting example indeed - and I argue that it is not broken at all, you were just using it wrong! :)
它失败了,因为这些文件描述符没有指向任何东西!正常的默认文件描述符是标准输入
0
、标准输出1
和标准错误流2
。由于您的脚本没有打开任何其他文件,因此没有其他有效的文件描述符。您可以使用 exec 在 bash 中打开文件。这是对示例的修改:现在我们将运行它:
如您所见,额外的输出已发送到请求的文件。
It's failing because those file descriptors don't point to anything! The normal default file descriptors are the standard input
0
, the standard output1
, and the standard error stream2
. Since your script isn't opening any other files, there are no other valid file descriptors. You can open a file in bash usingexec
. Here's a modification of your example:And now we'll run it:
As you can see, the extra output was sent to the requested files.
添加到 来自 rsp 的答案 和 回答来自 @MattClimbs。
您可以通过尝试提前重定向到文件描述符来测试文件描述符是否打开,如果失败,则将所需的编号文件描述符打开为
/dev/null
之类的内容。我定期在脚本中执行此操作,并利用附加文件描述符来传回return #
之外的附加详细信息或响应。script.sh
stderr 被重定向到
/dev/null
以丢弃可能的bash: #: Bad file detector
响应,并且||
为用于在前一个命令以非零状态退出时处理以下命令exec #>/dev/null
。如果文件描述符已打开,则两个测试将返回零状态,并且不会执行 exec ... 命令。在没有任何重定向的情况下调用脚本会产生:
在这种情况下,
a
和test
的重定向将发送到/dev/null
使用定义的重定向调用脚本会产生:
第一个重定向
3>temp.txt
覆盖文件temp.txt
,而4>则覆盖文件
方法temp.txt
。 >temp.txt最后,如果您想要
/dev/null
以外的内容,您可以定义默认文件以在脚本中重定向到,或者您可以更改该脚本的执行 脚本并将这些额外的文件描述符重定向到您想要的任何位置。To add on to the answer from rsp and respond the question in the comments of that answer from @MattClimbs.
You can test if the file descriptor is open or not by attempting to redirect to it early and if it fails, open the desired numbered file descriptor to something like
/dev/null
. I do this regularly within scripts and leverage the additional file descriptors to pass back additional details or responses beyondreturn #
.script.sh
The stderr is redirected to
/dev/null
to discard the possiblebash: #: Bad file descriptor
response and the||
is used to process the following commandexec #>/dev/null
when the previous one exits with a non zero status. In the event that the file descriptor is already opened, the two tests would return a zero status and theexec ...
command would not be executed.Calling the script without any redirections yields:
In this case, the redirections for
a
andtest
are shipped off to/dev/null
Calling the script with a redirection defined yields:
The first redirection
3>temp.txt
overwrites the filetemp.txt
while4>>temp.txt
appends to the file.In the end, you can define default files to redirect to within the script if you want something other than
/dev/null
or you can change the execution method of the script and redirect those extra file descriptors anywhere you want.