脑壳迟钝的生物研讨者 發表於 2025-6-8 09:10:13

一文带你简单理解linux中的输入输出重定向

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">linux简单理解输入输出重定向</a></li><ul class="second_class_ul"><li><a href="#_lab2_0_0">输出重定向原理</a></li><li><a href="#_lab2_0_1">输出重定向</a></li><li><a href="#_lab2_0_2">输入重定向</a></li><li><a href="#_lab2_0_3">重定向的顺序</a></li></ul><li><a href="#_label1">总结</a></li><ul class="second_class_ul"></ul></ul></div><p class="maodian"><a name="_label0"></a></p><h2>linux简单理解输入输出重定向</h2>
<p><code>linux</code>重定向可以分为输出重定向和输入重定向。在系统中除了标准输入、标准输出、错误输出以外,其他的重定向,都需要指定文件描述符<code>id</code>,也可以称为句柄。</p>
<p>在<code>linux</code>中,可以通过查看<code>/proc/进程pid号/fd/</code>下的文件来查看该进程有多少个<code>fd</code>,比如:</p>
<div class="jb51code"><pre class="brush:bash;"># ls -l /proc/$$/fd/
total 0
lrwx------. 1 root root 64 Jun3 22:22 0 -&gt; /dev/pts/0
lrwx------. 1 root root 64 Jun3 22:22 1 -&gt; /dev/pts/0
lrwx------. 1 root root 64 Jun3 22:22 2 -&gt; /dev/pts/0
#
</pre></div>
<p>上述命令中<code>$$</code>表示该进程本身的<code>pid</code>,而该<code>fd</code>文件下,可以看到有<code>0</code>、<code>1</code>、<code>2</code> 三个文件,都指向<code>/dev/pts/0</code>,这是一个字符设备文件,表示当前的终端。</p>
<p class="maodian"><a name="_lab2_0_0"></a></p><h3>输出重定向原理</h3>
<p>对于系统而言,任何一个程序都有2个输出,分别是:</p>
<ul><li>文件描述符为1的标准输出</li><li>文件描述符为2的错误输出</li></ul>
<p>所以,当执行一个命令输出重定向的时候,比如:</p>
<div class="jb51code"><pre class="brush:bash;">command &gt;&gt; filename
</pre></div>
<p>上述命令是将<code>command</code>命令执行的标准输出结果重定向到<code>filename</code>文件中,你可以理解为在执行该命令的时候,其进程标准输出文件描述符<code>1</code>由默认的<code>/dev/pts/0</code>指向了<code>filename</code>文件,为此可以来写一个程序来试试。</p>
<p>程序如下:</p>
<div class="jb51code"><pre class="brush:bash;">#!/bin/bash

echo "pid: $$" &gt;&amp;2

while true
do
      echo 123
      sleep 3
done
</pre></div>
<p>这个程序非常简单,首先现输出该脚本的<code>pid</code>到错误输出上,即文件描述符为2,而后定一个无限循环,每隔3秒输出<code>123</code>。</p>
<p>在执行的时候,将该文件输出重定向到<code>filename</code>中。</p>
<div class="jb51code"><pre class="brush:bash;"># bash test.sh &gt;&gt; filename
pid: 42699</pre></div>
<p>程序执行会卡主,这是正常的,这是因为有无限循环,每隔3秒会输出<code>123</code>字符串至标准输出上。</p>
<p>而后打开一个新的终端,查看<code>42699</code>的<code>fd</code>文件信息。</p>
<div class="jb51code"><pre class="brush:bash;"># ls -l /proc/42699/fd/
total 0
lrwx------. 1 root root 64 Jun3 23:41 0 -&gt; /dev/pts/0
l-wx------. 1 root root 64 Jun3 23:41 1 -&gt; /root/fd/filename
lrwx------. 1 root root 64 Jun3 23:41 2 -&gt; /dev/pts/0
lr-x------. 1 root root 64 Jun3 23:41 255 -&gt; /root/fd/test.sh
#
</pre></div>
<p>通过上面执行的结果可以发现,其标准输出,文件描述符为<code>1</code>已经指向一个文件:<code>/root/fd/filename</code>,而非终端字符设备。所以说,在上面案例中,重定向后,实际上是将句柄给改写到文件中,所以屏幕没有输出任何数据。基于此,标准输入和错误输出的原理也是这样的。</p>
<p class="maodian"><a name="_lab2_0_1"></a></p><h3>输出重定向</h3>
<p>输出重定向一般格式为:<code>&gt;[|]filename</code>,其中<code>n</code>是文件描述符,<code>&gt;|</code>表示覆盖保护绕过机制,不过一般不用,即常用的写法为:<code>&gt;filename</code>。</p>
<p>比如,将字符串<code>abcdef</code>的标准输出重定向到<code>filename</code>中:</p>
<div class="jb51code"><pre class="brush:bash;">echo "abcdef" 1&gt; filename
</pre></div>
<p>默认情况下,<code>1&gt;</code>可以缩略写为<code>&gt;</code>,这也是常用的写法:</p>
<div class="jb51code"><pre class="brush:bash;">echo "abcde" &gt; filename
</pre></div>
<p>将命令<code>dasdsada</code>的错误结果写入到文件<code>filename</code>中:</p>
<div class="jb51code"><pre class="brush:bash;">dasdsada 2&gt; filename
</pre></div>
<p>因为没有这个命令,所以一定会报错<code>-bash: dasdsada: command not found</code>。</p>
<p>对于该重定向而言,它在执行前会清理掉原始内容,从而写入新的内容。</p>
<p>除此之外,还有追加重定向,其格式为<code>&gt;&gt;filename</code>,其中<code>n</code>是文件描述符,<code>&gt;&gt;</code>表示追加,它不会清理掉原有内容。比如将<code>abc</code>追加到文件<code>filename</code>中。</p>
<div class="jb51code"><pre class="brush:bash;">echo 123 1&gt;&gt; filename
</pre></div>
<p>同样的,也可以将<code>1&gt;&gt;</code>缩略写为<code>&gt;&gt;</code>。</p>
<p>将命令<code>aaa</code>的错误结果追加写入到文件<code>filename</code>中:</p>
<div class="jb51code"><pre class="brush:bash;">aaa 2&gt;&gt; filename
</pre></div>
<p class="maodian"><a name="_lab2_0_2"></a></p><h3>输入重定向</h3>
<p>在<code>linux</code>系统中,任何一个程序的标准输入文件描述符<code>id</code>为<code>0</code>,而输入重定向一般格式为<code>&lt;filename</code>,其中<code>n</code>表示文件描述符。</p>
<p>所以,如下命令:</p>
<div class="jb51code"><pre class="brush:bash;">cat 0&lt; filename       
</pre></div>
<p>它的意思是将<code>filename</code>文件的内容作为标准输入提供给<code>cat</code>调用,结果是打印了<code>filename</code>的内容,上述命令中的<code>0&lt;</code>可以缩略写为<code>&lt;</code>。</p>
<p>不仅如此,还能在循环中,搭配<code>read</code>,将文件的信息按行读取。</p>
<p>比如有以下文件内容:</p>
<div class="jb51code"><pre class="brush:bash;"># cat filename
aa
bb
cc
dd
#
</pre></div>
<p>有脚本内容如下:</p>
<div class="jb51code"><pre class="brush:bash;">#!/bin/bash

while read ll
do
      echo read_at: ${ll}
done 0&lt; filename
</pre></div>
<p>上述脚本表示将<code>filename</code>文件作为输入源,供<code>while</code>调用,按照的方式是<code>read</code>命令按行调用,将行内容赋值到<code>ll</code>变量上,这个变量名称可以自定义。</p>
<p>其结果为:</p>
<div class="jb51code"><pre class="brush:bash;"># bash test.sh
read_at: aa
read_at: bb
read_at: cc
read_at: dd
#
</pre></div>
<p>输入重定向还有一种表达方式是<code>here-documents</code>。表示多行定义输入,其语法为:</p>
<div class="jb51code"><pre class="brush:bash;">command &lt;&lt;word
here doc
word
</pre></div>
<p>其中<code>n</code>表示文件操作符,如果省略,则使用默认的文件操作符<code>0</code>,而后是关键字<code>&lt;&lt;</code>,<code>word</code>是自定义的结束符,让后续读取到<code>word</code>后停止读取。</p>
<p>比如:</p>
<div class="jb51code"><pre class="brush:bash;">cat &lt;&lt;END
123
456
789
END
</pre></div>
<p>输出的结果为</p>
<blockquote><p>123<br />456<br />789</p></blockquote>
<p>上述命令中的<code>END</code>是可以替换为任何字符串的,只要后续出现该字符串就会停止输入,注意这个字符,一定是要在单独的一行出现的,不能有其他字符,包括空格。</p>
<p><code>here-documents</code>会进行命令替换,变量替换等,比如有如下命令:</p>
<div class="jb51code"><pre class="brush:bash;"># x=123
# cat &lt;&lt;END
${x}
$(pwd)
aa
bb
cc
END
</pre></div>
<p>输出的结果会先进行命令替换,变量替换等,比如将<code>${x}</code>替换为<code>123</code>,将<code>$(pwd)</code>替换为当前目录。如果不想其被替换,则需要添加单引号进行转义。</p>
<p class="maodian"><a name="_lab2_0_3"></a></p><h3>重定向的顺序</h3>
<p>注意看,如下有2条命令:</p>
<div class="jb51code"><pre class="brush:bash;">ls -l &gt; filename 2&gt;&amp;1
</pre></div>
<p>和</p>
<div class="jb51code"><pre class="brush:csharp;">ls -l 2&gt;&amp;1 &gt; filename
</pre></div>
<p>所执行的结果是完全不一样的,第一条命令是将标准输出和错误输出都写入到<code>filename</code>中,而第二条则是将错误输出输出到屏幕上,而将标准输出写入到文件中,如果不是很明白,可以尝试拆解一下命令:</p>
<p>首先,命令的前提是:标准输出 和 错误输出 都在某个设备上,比如<code>pty</code>终端,假设设备为<code>/dev/pts/0</code>,所以文件描述符信息为:</p>
<ul><li><code>1 -&gt; /dev/pts/0</code></li><li><code>2 -&gt; /dev/pts/0</code></li></ul>
<p>然后第一条命令,<code>ls -l &gt; filename</code> 其实是将文件描述符<code>1</code>给指向了文件<code>filename</code>,这个时候,文件描述符信息为:</p>
<ul><li><code>1 -&gt; filename </code></li><li><code>2 -&gt; /dev/pts/0</code></li></ul>
<p>这个时候再使用<code>2&gt;&amp;1</code>表示将错误消息也写入到文件描述符为<code>1</code>的文件中,即<code>filename</code>,所以最后的文件描述符为:</p>
<ul><li><code>1 -&gt; filename </code></li><li><code>2 -&gt; filename </code></li></ul>
<p>而第二条命令,一开始都是输出到某个设备上:</p>
<ul><li><code>1 -&gt; /dev/pts/0</code></li><li><code>2 -&gt; /dev/pts/0</code></li></ul>
<p>这个时候,它将错误输出指向<code>2&gt;&amp;1</code>,而文件描述符为<code>1</code>是<code>/dev/pts/0</code>,所以这第一步,就没动过,而第二部则是将标准输出指向<code>filename</code>,即:</p>
<ul><li><code>1 -&gt; filename </code></li><li><code>2 -&gt; /dev/pts/0</code></li></ul>
<p>所以第二条命令,错误输出输出到屏幕上,而将标准输出写入到文件中。</p>
<p class="maodian"><a name="_label1"></a></p><h2>总结</h2>
<p>要理解输入输出重定向,一定要理解文件的描述符!对于每个进程,都会产生3个默认的文件描述符,分别是 标准输入、标准输出 和 错误输出,用文件描述符<code>id</code>:<code>0</code>、<code>1</code>、<code>2</code> 来表示。</p>
<p>其重定向就是修改这些文件描述符的指向,将他们指向到文件中即可。</p>
<p>对于输入重定向而言,有一种表达方式是<code>here-documents</code>,它可以和输出重定向结合,将终端中输入的文件写入到文件中,比如:</p>
<div class="jb51code"><pre class="brush:bash;">cat 1&gt;&gt; filename 0&lt;&lt;END
a
b
c
d
e
END
</pre></div>
<p>表示将终端输入的<code>a</code>、<code>b</code>... <code>e</code>字符提供给<code>cat</code>命令,而<code>cat</code>将内容重定向到文件<code>filename</code>中。可以发现上面有<code>1&gt;&gt;</code>表示将标准输出的内容重定向到某个文件,<code>0&lt;&lt;</code>表示将标准输入的内容重定向到某个文件,上述命令中的<code>1&gt;&gt;</code>可以缩写为<code>&gt;&gt;</code>,而<code>0&lt;&lt;</code>可以缩写为<code>&lt;&lt;</code>。</p>
頁: [1]
查看完整版本: 一文带你简单理解linux中的输入输出重定向