小面大强 發表於 2023-5-29 00:00:00

Bash中文件描述符的详细介绍

<p>
        <span><strong>前言</strong></span></p>
<p>
        linux将所有内核对象当做文件来处理,系统用一个size_t类型来表示一个文件对象,比如对于文件描述符0就表示系统的标准输入设备stdin,通常情况下stdin的值为键盘,如read命令就默认从stdin读取数据,当然stdin的值是可以改变的,比如将其改成其他文件,这样的话想read等命令就会默认从相应的文件读取数据了。</p>
<p>
        简单地说,一个文件描述符可以和一个文件挂钩,一旦挂钩就可以通过取地址运算符&amp;获得该文件的句柄,比如&amp;0就可以获得stdin设备在内存中的句柄(设备在系统中也被当做文件处理),可以这样理解,如果是一个shell中的普通变量var,可以通过$var的形式获得该变量所代表的值,而对于一个文件描述符fd,则可以通过&amp;fd的形式获得文件描述符指向的文件的句柄,而这个句柄可以简单地理解成该文件的路径。</p>
<p>
        在 shell 编程里经常会用到重定向操作, 它本质上是对文件描述符进行操作, 本文会对 shell 脚本里的文件描述符做一个详细的介绍.</p>
<p>
        <span><strong>默认标准文件描述符</strong></span></p>
<p>
        每个进程启动时默认都会有三个标准的文件描述符:</p>
<ul>
<li>
                stdin 0 号描述符, 代表输入设备, 进程从它读入数据;</li>
        <li>
                stdout 1 号描述符, 进程往其中写入数据;</li>
        <li>
                stderr 2 号描述符, 进程会往其中写入错误信息;</li>
</ul>
<p>
        这三个描述符默认是对应同一个 tty 设备, 这样我们便可以在终端中输入数据和获取进程的输出.</p>
<p>
        默认的文件描述符也是可以被替换的, 例如我们可以替换掉 stdout 到一个文件, 这样命令的输出就不是打印到终端, 而是被输出到文件中:</p>
<p>
        <img title="Bash中文件描述符的详细介绍" alt="Bash中文件描述符的详细介绍" src="https://zhuji.jb51.net/uploads/img/202305/de57843e909b42d7c1e9fdfa0e142402.jpg"></p>
<p>
        在上面的 demo 中, 我们先是通过 exec 1 &gt; /tmp/stdout 把 stdout 指向了文件 /tmp/stdout, 紧接着我们执行了两条命令 ls 和 pwd, 可以看到此时终端已经没有了命令的输出. 当我们通过 exec 1 &gt;&amp;2 恢复 stdout 后, 可以发现文件 /tmp/stdout 里存储了之前命令的输出.</p>
<p>
        其中 exec 是一个 bash 内置命令, 不同于在终端中执行命令时会 fork 一个子进程, 通过 exec 执行的命令会直接修改当前的 shell 进程, 可以通过它执行命令来修改当前 shell 的 context.</p>
<p>
        如果你想使坏的话可以在别人的 ~/.bashrc 里加入 exec 1 &gt; /tmp/stdout, 这样新开的所有的终端窗口里都看不到命令的输出, 要是因此被打概不负责 :) .</p>
<p>
        <span><strong>文件描述符的操作</strong></span></p>
<p>
        shell 中对文件描述符的操作由三部分组成: (left, operation, right):</p>
<ul>
<li>
                left 可以是 0-9 的数字, 代表第 n 号文件描述符;<br>
                     left 还可以为 &amp;, 表示同时操作 stdout 和 stderr</li>
        <li>
                right 可以是文件名或 0-9 的数字, 当 right 是数字时必须要加上 &amp; 符号, 表示引用第 n 号文件描述符;<br>
                     right 还可以为 &amp;-, 此时表示关闭 left 描述符, 例如 2&lt;&amp;- 表示关闭 stderr;</li>
        <li>
                operation 可以为 &lt; 或 &gt;;<br>
                     为 &lt; 时表示以读模式复制 right 到 left, 此时如果没有指定 left 的话, 则为默认值 0;<br>
                     当为 &gt; 表示以写模式复制 right 到 left, 此时如果没有指定 left 的话, 则为默认值 1;<br>
                     operation 和 left 之间不能有空格;<br>
                     当 right 为文件名时, operation 和 right 可以有空格, 否则也不能有空格;</li>
</ul>
<p>
        当存在多个文件描述符的操作时, 会按照从左往右的顺序依次执行. 例如通过命令 <code>cmd 3&gt;&amp;1 1&gt;&amp;2 2&gt;&amp;3 3&gt;&amp;- </code>就可以交换 stdin 和 stdout.</p>
<p>
        我们通过下面的例子来验证上面的文件描述符交换是否生效:</p>
<ul>
<li>
                首先把默认的 stderr 重定向到文件 /tmp/stderr 中, 这样在终端中就不会看到错误输出了;</li>
        <li>
                当交换完 stderr 和 stdout 后, 我们就可以在 /tmp/stderr 文件中看到命令的正常输出了;</li>
</ul>
<p>
        让我们来开始实验吧:</p>
<div class="jb51code">
        <div>
                <div class="syntaxhighlighterbash" id="highlighter_611232">
                        <div class="toolbar">
                                <span>?</span>
</div>
                        <table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
                                                        <div class="line number1 index0 alt2">
                                                                1</div>
                                                        <div class="line number2 index1 alt1">
                                                                2</div>
                                                        <div class="line number3 index2 alt2">
                                                                3</div>
                                                        <div class="line number4 index3 alt1">
                                                                4</div>
                                                        <div class="line number5 index4 alt2">
                                                                5</div>
                                                        <div class="line number6 index5 alt1">
                                                                6</div>
                                                </td>
                                                <td class="code">
                                                        <div class="container">
                                                                <div class="line number1 index0 alt2">
                                                                        <code class="bash plain">➜ </code><code class="bash functions">test</code> <code class="bash functions">exec</code> <code class="bash plain">2&gt; </code><code class="bash plain">/tmp/stderr</code>
</div>
                                                                <div class="line number2 index1 alt1">
                                                                        <code class="bash plain">➜ </code><code class="bash functions">test</code> <code class="bash functions">ls</code>
</div>
                                                                <div class="line number3 index2 alt2">
                                                                        <code class="bash plain">a.txt</code>
</div>
                                                                <div class="line number4 index3 alt1">
                                                                        <code class="bash plain">➜ </code><code class="bash functions">test</code> <code class="bash functions">ls</code> <code class="bash plain">3&gt;&amp;1 1&gt;&amp;2 2&gt;&amp;3 3&gt;&amp;-</code>
</div>
                                                                <div class="line number5 index4 alt2">
                                                                        <code class="bash plain">➜ </code><code class="bash functions">test</code> <code class="bash functions">cat</code> <code class="bash plain">/tmp/stderr</code>
</div>
                                                                <div class="line number6 index5 alt1">
                                                                        <code class="bash plain">a.txt</code>
</div>
                                                        </div>
                                                </td>
                                        </tr></tbody></table>
</div>
        </div>
</div>
<p>
        和我们的预期时一致的!</p>
<p>
        <span><strong>一些示例</strong></span></p>
<p>
        用文件重载 stdin :</p>
<div class="jb51code">
        <div>
                <div class="syntaxhighlighterbash" id="highlighter_999995">
                        <div class="toolbar">
                                <span>?</span>
</div>
                        <table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
                                                        <div class="line number1 index0 alt2">
                                                                1</div>
                                                        <div class="line number2 index1 alt1">
                                                                2</div>
                                                        <div class="line number3 index2 alt2">
                                                                3</div>
                                                        <div class="line number4 index3 alt1">
                                                                4</div>
                                                </td>
                                                <td class="code">
                                                        <div class="container">
                                                                <div class="line number1 index0 alt2">
                                                                        <code class="bash plain">➜ </code><code class="bash functions">test</code> <code class="bash functions">cat</code> <code class="bash plain">0&lt; a.txt</code>
</div>
                                                                <div class="line number2 index1 alt1">
                                                                        <code class="bash plain">hello</code>
</div>
                                                                <div class="line number3 index2 alt2">
                                                                        <code class="bash plain">➜ </code><code class="bash functions">test</code> <code class="bash functions">cat</code> <code class="bash plain">&lt; a.txt </code><code class="bash comments"># same with last command</code>
</div>
                                                                <div class="line number4 index3 alt1">
                                                                        <code class="bash plain">hello</code>
</div>
                                                        </div>
                                                </td>
                                        </tr></tbody></table>
</div>
        </div>
</div>
<p>
        把 stderr 和 stdout 都过滤掉</p>
<div class="jb51code">
        <div>
                <div class="syntaxhighlighterbash" id="highlighter_526921">
                        <div class="toolbar">
                                <span>?</span>
</div>
                        <table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
                                                        <div class="line number1 index0 alt2">
                                                                1</div>
                                                        <div class="line number2 index1 alt1">
                                                                2</div>
                                                        <div class="line number3 index2 alt2">
                                                                3</div>
                                                </td>
                                                <td class="code">
                                                        <div class="container">
                                                                <div class="line number1 index0 alt2">
                                                                        <code class="bash functions">ls</code> <code class="bash plain">not_exist 1&gt; </code><code class="bash plain">/dev/zero</code> <code class="bash plain">2&gt;&amp;1</code>
</div>
                                                                <div class="line number2 index1 alt1">
                                                                        <code class="bash comments"># another way</code>
</div>
                                                                <div class="line number3 index2 alt2">
                                                                        <code class="bash functions">ls</code> <code class="bash plain">not_exist &amp;&gt; </code><code class="bash plain">/dev/zero</code>
</div>
                                                        </div>
                                                </td>
                                        </tr></tbody></table>
</div>
        </div>
</div>
<p>
        处理上一个命令的错误输出:</p>
<div class="jb51code">
        <div>
                <div class="syntaxhighlighterbash" id="highlighter_540810">
                        <div class="toolbar">
                                <span>?</span>
</div>
                        <table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
                                                        <div class="line number1 index0 alt2">
                                                                1</div>
                                                        <div class="line number2 index1 alt1">
                                                                2</div>
                                                        <div class="line number3 index2 alt2">
                                                                3</div>
                                                        <div class="line number4 index3 alt1">
                                                                4</div>
                                                        <div class="line number5 index4 alt2">
                                                                5</div>
                                                </td>
                                                <td class="code">
                                                        <div class="container">
                                                                <div class="line number1 index0 alt2">
                                                                        <code class="bash plain">➜ blog git:(hexo) </code><code class="bash functions">ls</code> <code class="bash plain">not_exist 2&gt;&amp;1 | </code><code class="bash functions">sed</code> <code class="bash string">'s/not_exist/error/g'</code>
</div>
                                                                <div class="line number2 index1 alt1">
                                                                        <code class="bash functions">ls</code><code class="bash plain">: error: no such </code><code class="bash functions">file</code> <code class="bash plain">or directory</code>
</div>
                                                                <div class="line number3 index2 alt2">
                                                                        <code class="bash comments"># another way</code>
</div>
                                                                <div class="line number4 index3 alt1">
                                                                        <code class="bash plain">➜ blog git:(hexo) </code><code class="bash functions">ls</code> <code class="bash plain">not_exist |&amp; </code><code class="bash functions">sed</code> <code class="bash string">'s/not_exist/error/g'</code>
</div>
                                                                <div class="line number5 index4 alt2">
                                                                        <code class="bash functions">ls</code><code class="bash plain">: error: no such </code><code class="bash functions">file</code> <code class="bash plain">or directory</code>
</div>
                                                        </div>
                                                </td>
                                        </tr></tbody></table>
</div>
        </div>
</div>
<p>
        把标准输出转入到错误输出上: echo hello 1&gt;&amp;2</p>
<p>
        <span><strong>process substitution</strong></span></p>
<p>
        在 bash 中提供了两个特殊的操作, 它们都可以被直接当成文件名使用:</p>
<ul>
<li>
                &lt;(cmd) : 可以看作时一个可读文件, cmd 命令的输出是这个文件的内容;</li>
        <li>
                &gt;(cmd) : 可以看作时一个可写文件, cmd 会接受输入并进行处理;</li>
</ul>
<p>
        <span><strong>示例</strong></span></p>
<p>
        利用 &lt;(cmd) 来验证一对公私钥是否匹配:</p>
<div class="jb51code">
        <div>
                <div class="syntaxhighlighterbash" id="highlighter_81106">
                        <div class="toolbar">
                                <span>?</span>
</div>
                        <table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
                                                        <div class="line number1 index0 alt2">
                                                                1</div>
                                                        <div class="line number2 index1 alt1">
                                                                2</div>
                                                </td>
                                                <td class="code">
                                                        <div class="container">
                                                                <div class="line number1 index0 alt2">
                                                                        <code class="bash plain">➜ blog git:(hexo) </code><code class="bash functions">diff</code> <code class="bash plain">&lt;(</code><code class="bash functions">ssh</code><code class="bash plain">-keygen -y -e -f ~/.</code><code class="bash functions">ssh</code><code class="bash plain">/id_rsa</code><code class="bash plain">) &lt;(</code><code class="bash functions">ssh</code><code class="bash plain">-keygen -y -e -f ~/.</code><code class="bash functions">ssh</code><code class="bash plain">/id_rsa</code><code class="bash plain">.pub)</code>
</div>
                                                                <div class="line number2 index1 alt1">
                                                                        <code class="bash plain">➜ blog git:(hexo)</code>
</div>
                                                        </div>
                                                </td>
                                        </tr></tbody></table>
</div>
        </div>
</div>
<p>
        利用 &gt;(cmd) 来对错误信息进行处理, 同时保证错 stderr 信息不回变成 stdout:</p>
<div class="jb51code">
        <div>
                <div class="syntaxhighlighterbash" id="highlighter_356544">
                        <div class="toolbar">
                                <span>?</span>
</div>
                        <table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
                                                        <div class="line number1 index0 alt2">
                                                                1</div>
                                                        <div class="line number2 index1 alt1">
                                                                2</div>
                                                        <div class="line number3 index2 alt2">
                                                                3</div>
                                                </td>
                                                <td class="code">
                                                        <div class="container">
                                                                <div class="line number1 index0 alt2">
                                                                        <code class="bash plain">➜ blog git:(hexo) </code><code class="bash functions">ls</code> <code class="bash plain">not_exist 2&gt; &gt;(</code><code class="bash functions">sed</code> <code class="bash string">'s/not_exist/keep_error/g'</code><code class="bash plain">)</code>
</div>
                                                                <div class="line number2 index1 alt1">
                                                                        <code class="bash functions">ls</code><code class="bash plain">: keep_error: no such </code><code class="bash functions">file</code> <code class="bash plain">or directory</code>
</div>
                                                                <div class="line number3 index2 alt2">
                                                                        <code class="bash plain">➜ blog git:(hexo)</code>
</div>
                                                        </div>
                                                </td>
                                        </tr></tbody></table>
</div>
        </div>
</div>
<p>
        <span><strong>总结</strong></span></p>
<p>
        以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。</p>
<p>
        <strong>references</strong></p>
<ul>
<li>
                file descriptors in bourne shell
</li>
        <li>
                process substitution
</li>
</ul>
<p>
        原文链接:https://hiberabyss.github.io/2018/04/05/shell-file-description/</p>
頁: [1]
查看完整版本: Bash中文件描述符的详细介绍