<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[向东博客 专注WEB应用 构架之美 --- 构架之美，在于尽态极妍 | 应用之美，在于药到病除]]></title> 
<link>http://www.jackxiang.com/index.php</link> 
<description><![CDATA[赢在IT，Playin' with IT,Focus on Killer Application,Marketing Meets Technology.]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[向东博客 专注WEB应用 构架之美 --- 构架之美，在于尽态极妍 | 应用之美，在于药到病除]]></copyright>
<item>
<link>http://www.jackxiang.com/post//</link>
<title><![CDATA[[实践OK]linux shell 中21含义的指针理解,如何把错误定向到空文件？]]></title> 
<author>jack &lt;xdy108@126.com&gt;</author>
<category><![CDATA[WEB2.0]]></category>
<pubDate>Fri, 19 Sep 2008 10:04:05 +0000</pubDate> 
<guid>http://www.jackxiang.com/post//</guid> 
<description>
<![CDATA[ 
	<textarea name="code" class="php" rows="15" cols="100">
为什么2&gt;&amp;1要放在后面
考虑如下一条shell命令
nohup java -jar app.jar &gt;log 2&gt;&amp;1 &amp;
(最后一个&amp;表示把条命令放到后台执行，不是本文重点，不懂的可以自行Google)
为什么2&gt;&amp;1一定要写到&gt;log后面，才表示标准错误输出和标准输出都定向到log中？
我们不妨把1和2都理解是一个指针,然后来看上面的语句就是这样的：

本来1-----&gt;屏幕 （1指向屏幕）
执行&gt;log后， 1-----&gt;log (1指向log)
执行2&gt;&amp;1后， 2-----&gt;1 (2指向1，而1指向log,因此2也指向了log)
再来分析下

nohup java -jar app.jar 2&gt;&amp;1 &gt;log &amp;
本来1-----&gt;屏幕 （1指向屏幕）
执行2&gt;&amp;1后， 2-----&gt;1 (2指向1，而1指向屏幕,因此2也指向了屏幕)
执行&gt;log后， 1-----&gt;log (1指向log，2还是指向屏幕)
所以这就不是我们想要的结果。
</textarea><br/>实践：<br/>ls AlTest1.err &gt; log&nbsp;&nbsp; #有此文件，也就是输出指向屏幕给输入到了log文件<br/><br/>ls AlTest1.err2 &gt; log #没有此文件，输出的是错误2，正常输出的1指向屏幕1-----&gt;log (1指向log)，2也是指向幕，于是将错输出到了屏。<br/>ls: AlTest1.err2: No such file or directory<br/><br/>ls AlTest1.err2 &gt; log 2&gt;&amp;1 # (2指向1，而1指向log,因此2也指向了log)错误也指向了文件，于是此次的错误没有输出。<br/>cat log<br/>ls: AlTest1.err2: No such file or directory<br/><br/>2&gt;&amp;1 换个位置的情况，如下：<br/>ls AlTest1.err2&nbsp;&nbsp;2&gt;&amp;1 &gt; log&nbsp;&nbsp;#输出：ls: AlTest1.err2: No such file or directory ，原因是2还是指向屏幕<br/>本来1-----&gt;屏幕 （1指向屏幕）<br/>执行2&gt;&amp;1后， 2-----&gt;1 (2指向1，而1指向屏幕,因此2也指向了屏幕)<br/>执行&gt;log后， 1-----&gt;log (1指向log，2还是指向屏幕)<br/><br/>如果文件存在，1输出指向了log，所以下成这个语句是能输出到文件中的：<br/>ls AlTest1.err&nbsp;&nbsp;2&gt;&amp;1 &gt; log<br/>cat log<br/>AlTest1.err<br/><br/>将上述的正确输出的文件换成/dev/null, 即可实现正确错误均给导入空洞。<br/><br/>来自：<a href="https://www.jb51.net/article/169778.htm" target="_blank">https://www.jb51.net/article/169778.htm</a> <br/><br/><br/>0:标准输出，1:标准输入，2:标准错误。 输出可以输出到控制台，也可输出到文件，从左向右的顺序，这样可理解后面思考题。<br/>2&gt;&amp;1表明将文件描述2（标准错误输出）的内容重定向到文件描述符1（标准输出），为什么1前面需要&amp;？当没有&amp;时，1会被认为是一个普通的文件，有&amp;表示重定向的目标不是一个文件，而是一个文件描述符。在前面我们知道，test.sh &gt;log.txt又将文件描述符1的内容重定向到了文件log.txt，那么最终标准错误也会重定向到log.txt。<br/>cat test.sh<br/><textarea name="code" class="php" rows="15" cols="100">
#!/bin/bash
date
while true
do
&nbsp;&nbsp;&nbsp;&nbsp;sleep 2
&nbsp;&nbsp;&nbsp;&nbsp;whatthis
&nbsp;&nbsp;&nbsp;&nbsp;echo -e &quot;std output&quot;
done
</textarea><br/><br/>#./test.sh &gt; log.txt<br/>./test.sh:行6: whatthis: 未找到命令<br/>./test.sh:行6: whatthis: 未找到命令<br/>./test.sh:行6: whatthis: 未找到命令<br/><br/><textarea name="code" class="php" rows="15" cols="100">
#ps -ef&#124;grep test.sh
root&nbsp;&nbsp;&nbsp;&nbsp; 28385 27575&nbsp;&nbsp;0 16:32 pts/2&nbsp;&nbsp;&nbsp;&nbsp;00:00:00 /bin/bash ./test.sh
root&nbsp;&nbsp;&nbsp;&nbsp; 28455 27703&nbsp;&nbsp;0 16:33 pts/3&nbsp;&nbsp;&nbsp;&nbsp;00:00:00 grep --color=auto test.sh

#cd /proc/28385/fd

/proc/28385/fd]

/proc/28385/fd
#ll
总用量 0
lrwx------ 1 root root 64 10月 30 16:33 0 -&gt; /dev/pts/2
l-wx------ 1 root root 64 10月 30 16:33 1 -&gt; /data/codesdev/testdemo/shell/log.txt
lrwx------ 1 root root 64 10月 30 16:33 2 -&gt; /dev/pts/2
</textarea><br/><br/><br/>如果写成这样：#./test.sh &gt; log.txt 2&gt;&amp;1 ，标准输出和错误输出均指向了 /data/codesdev/testdemo/shell/log.txt:<br/>#ll<br/>总用量 0<br/>lrwx------ 1 root root 64 10月 30 16:38 0 -&gt; /dev/pts/2<br/>l-wx------ 1 root root 64 10月 30 16:38 1 -&gt; /data/codesdev/testdemo/shell/log.txt<br/>l-wx------ 1 root root 64 10月 30 16:37 2 -&gt; /data/codesdev/testdemo/shell/log.txt<br/>lr-x------ 1 root root 64 10月 30 16:38 255 -&gt; /data/codesdev/testdemo/shell/test.sh<br/>w<br/>xiangdon pts/2&nbsp;&nbsp;&nbsp;&nbsp;202.108.16.78&nbsp;&nbsp;&nbsp;&nbsp;15:37&nbsp;&nbsp; 47.00s&nbsp;&nbsp;0.10s&nbsp;&nbsp;0.03s sshd: xiangdong [priv] #程序运行终端<br/>xiangdon pts/3&nbsp;&nbsp;&nbsp;&nbsp;202.108.16.78&nbsp;&nbsp;&nbsp;&nbsp;15:40&nbsp;&nbsp;&nbsp;&nbsp;0.00s&nbsp;&nbsp;0.08s&nbsp;&nbsp;0.03s sshd: xiangdong [priv]&nbsp;&nbsp;#另一个终端<br/>pkill -KILL -t pts/3 <br/><br/>cd /proc/28725/fd<br/><br/><br/><br/>我们总结一下前面的内容：<br/><br/>程序运行后会打开三个文件描述符，分别是标准输入，标准输出和标准错误输出。<br/>在调用脚本时，可使用2&gt;&amp;1来将标准错误输出重定向。<br/>只需要查看脚本的错误时，可将标准输出重定向到文件，而标准错误会打印在控制台，便于查看。<br/>&gt;&gt;log.txt会将重定向内容追加到log.txt文件末尾。<br/>通过查看/proc/进程id/fd下的内容，可了解进程打开的文件描述符信息。<br/>来自：https://mp.weixin.qq.com/s/-9uO7lc_xfvpZxEsaez7HQ<br/><br/>#./test.sh 2&gt;&amp;1 &gt;log.txt&nbsp;&nbsp;#思考题：为何这样一样有错误输出呢？<br/>思考<br/>下面的调用会将标准错误输出重定向到文件中吗？为什么？<br/>./test.sh 2&gt;&amp;1 &gt;log.txt <br/><br/>./test.sh:行6: whatthis: 未找到命令<br/>./test.sh:行6: whatthis: 未找到命令<br/>./test.sh:行6: whatthis: 未找到命令<br/>./test.sh:行6: whatthis: 未找到命令<br/>./test.sh:行6: whatthis: 未找到命令<br/>./test.sh:行6: whatthis: 未找到命令<br/>原因2 -&gt; /dev/pts/2，并没有给指向/data/codesdev/testdemo/shell/log.txt：<br/>#ll /proc/29313/fd<br/>总用量 0<br/>lrwx------ 1 root root 64 10月 30 16:44 0 -&gt; /dev/pts/2<br/>l-wx------ 1 root root 64 10月 30 16:44 1 -&gt; /data/codesdev/testdemo/shell/log.txt<br/>lrwx------ 1 root root 64 10月 30 16:44 2 -&gt; /dev/pts/2<br/>lr-x------ 1 root root 64 10月 30 16:44 255 -&gt; /data/codesdev/testdemo/shell/test.sh<br/><br/>答案：解释一下思考题:不能。<br/>因为从左往右结合，现有2&gt;&amp;1，这个时候1指向的是控制台，这个时候也就将标准错误2重定向控制台（相当于不起作用），最后才将标准输出重定向到log.txt。结果就是只有标准输出重定向到了log.txt。可以通过同样的方法查看文件描述符的指向。<br/>从左往右结合：也就是说先有输出（2&amp;&gt;1:标准错误输出到文件1输出）到控制台（不起作用，此时输出是控制台。），然后再定向到log.txt文件,而不是先输出到Log文件，之前是test.sh &gt;log.txt将文件描述符1的内容重定向到了文件log.txt，那么最终标准错误也会重定向到log.txt，现在是直接将错误给输出到终端了。<br/>&gt; 就是1，也就是说将标准输出到一个文件，它就是1，而./test.sh 2&gt;&amp;1 &gt;log.txt ，的1是输出到默认的终端terminal了，而./test.sh &gt;log.txt 2&gt;&amp;1里的输出1到文件了，后面的1也就跟着到了log.txt文件。<br/>===============================================================================================<br/><br/><div class="code">find -name &quot;*.jpg&quot; &#124; awk -F &quot;/&quot; &#039;&#123;print &quot;curl -F userfile=@/data1/app4_data/&quot; $2 &quot;/&quot; $3 &quot; <br/>http://app.space.sina.com.cn/upload.php&quot; &quot; &gt;&gt; /home/xiangdong2/image_logs/&quot; $3 &quot;.txt&quot;&#125;&#039; &#124; sh</div>这个脚本放入:push_upload_img.sh <br/>中的时候出现：curl传输速度等的信息，我们就要用到：&gt;/dev/null&nbsp;&nbsp;2&gt;&amp;1&nbsp;&nbsp;&amp; 了<br/><br/><div class="code">sh push_upload_img.sh &gt;/dev/null 2&gt;&amp;1 &amp; </div><br/><br/><br/><br/><br/>详细解释：<br/>脚本是:<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nohup /mnt/Nand3/H2000G&nbsp;&nbsp;&gt;/dev/null&nbsp;&nbsp;2&gt;&amp;1&nbsp;&nbsp;&amp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对于&amp; 1 更准确的说应该是文件描述符 1,而1 一般代表的就是STDOUT_FILENO,实际上这个操作就是一个dup2(2)调用.他标准输出到all_result ,然后复制标准输出到文件描述符2(STDERR_FILENO),其后果就是文件描述符1和2指向同一个文件表项,也可以说错误的输出被合并了.其中0表示键盘输入 1表示屏幕输出 2表示错误输出.把标准出错重定向到标准输出,然后扔到/DEV/NULL下面去。通俗的说，就是把所有标准输出和标准出错都扔到垃圾桶里面。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;command &gt;out.file&nbsp;&nbsp;2&gt;&amp;1 &amp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;command &gt;out.file是将command的输出重定向到out.file文件，即输出内容不打印到屏幕上，而是输出到out.file文件中。2&gt;&amp;1 是将标准出错重定向到标准输出，这里的标准输出已经重定向到了out.file文件，即将标准出错也输出到out.file文件中。最后一个&amp; ， 是让该命令在后台执行。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;试想2&gt;1代表什么，2与&gt;结合代表错误重定向，而1则代表错误重定向到一个文件1，而不代表标准输出；<br/>换成2&gt;&amp;1，&amp;与1结合就代表标准输出了，就变成错误重定向到标准输出.<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;你可以用<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ls 2&gt;1测试一下，不会报没有2文件的错误，但会输出一个空的文件1；<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ls xxx 2&gt;1测试，没有xxx这个文件的错误输出到了1中；<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ls xxx 2&gt;&amp;1测试，不会生成1这个文件了，不过错误跑到标准输出了；<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ls xxx &gt;out.txt 2&gt;&amp;1, 实际上可换成 ls xxx 1&gt;out.txt 2&gt;&amp;1；重定向符号&gt;默认是1,错误和输出都传到out.txt了。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为何2&gt;&amp;1要写在后面？<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;command &gt; file 2&gt;&amp;1 <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 首先是command &gt; file将标准输出重定向到file中， 2&gt;&amp;1 是标准错误拷贝了标准输出的行为，也就是同样被重定向到file中，最终结果就是标准输出和错误都被重定向到file中。 <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;command 2&gt;&amp;1 &gt;file <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&gt;&amp;1 标准错误拷贝了标准输出的行为，但此时标准输出还是在终端。&gt;file 后输出才被重定向到file，但标准错误仍然保持在终端。<br/><br/>用strace可以看到： <br/>1. command &gt; file 2&gt;&amp;1 <br/>这个命令中实现重定向的关键系统调用序列是： <br/>open(file) == 3 <br/>dup2(3,1) <br/>dup2(1,2) <br/><br/>2. command 2&gt;&amp;1 &gt;file <br/>这个命令中实现重定向的关键系统调用序列是： <br/>dup2(1,2) <br/>open(file) == 3 <br/>dup2(3,1) <br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;可以考虑一下不同的dup2()调用序列会产生怎样的文件共享结构。请参考APUE 3.10, 3.12<br/><div class="code">command &gt; filename 把把标准输出重定向到一个新文件中<br/>command &gt;&gt; filename 把把标准输出重定向到一个文件中(追加)<br/>command 1 &gt; fielname 把把标准输出重定向到一个文件中<br/>command &gt; filename 2&gt;&amp;1 把把标准输出和标准错误一起重定向到一个文件中<br/>command 2 &gt; filename 把把标准错误重定向到一个文件中<br/>command 2 &gt;&gt; filename 把把标准输出重定向到一个文件中(追加)<br/>command &gt;&gt; filename 2&gt;&amp;1 把把标准输出和标准错误一起重定向到一个文件中(追加)<br/>command &lt; filename &gt;filename2 把command命令以filename文件作为标准输入，以filename2文件作为标准输出<br/>command &lt; filename 把command命令以filename文件作为标准输入<br/>command &lt;&lt; delimiter 把从标准输入中读入，直至遇到delimiter分界符<br/>command &lt;&amp;m 把把文件描述符m作为标准输入<br/>command &gt;&amp;m 把把标准输出重定向到文件描述符m中<br/>command &lt;&amp;- 把关闭标准输入</div><br/><br/>crontab实例：<br/><br/><div class="code">*/30 * * * *&nbsp;&nbsp;&nbsp;&nbsp;/opt/bin/php /opt/www/daemon/search/rsyncuserdata.php &gt; /dev/null 2&gt;&amp;1<br/>*/30 * * * *&nbsp;&nbsp;&nbsp;&nbsp;/opt/bin/php /opt/www/daemon/search/makeuserdata.php &gt; /dev/null 2&gt;&amp;1<br/>0 */1 * * *&nbsp;&nbsp;&nbsp;&nbsp; /opt/bin/php /opt/www/daemon/search/makeusernewdata.php &gt; /dev/null 2&gt;&amp;1<br/>0 0 */1 * *&nbsp;&nbsp;&nbsp;&nbsp; /opt/bin/php /opt/www/daemon/birthday/birthday.php &gt; /dev/null 2&gt;&amp;1<br/><br/><br/>#cut weblog by yunfeng@20080627<br/>55 */1 * * *&nbsp;&nbsp;/usr/bin/perl /usr/home/pro/scripts/getlog_space.pl &gt;/dev/null&nbsp;&nbsp;2&gt;&amp;1<br/><br/>#gzip the yesterday&#039;s weblog by yunfeng@20080627<br/>40 0 * * *&nbsp;&nbsp;/usr/home/pro/scripts/gzip_log.sh &gt;/dev/null&nbsp;&nbsp;2&gt;&amp;1<br/>#rsync weblog to info sys and product data third times by yunfeng@20080627<br/>#30 1 * * *&nbsp;&nbsp;/usr/bin/perl /usr/home/pro/scripts/trans.pl &gt;/dev/null&nbsp;&nbsp;2&gt;&amp;1<br/>#40 1 * * *&nbsp;&nbsp;/usr/bin/perl /usr/home/pro/scripts/trans.pl &gt;/dev/null&nbsp;&nbsp;2&gt;&amp;1<br/>#50 1 * * *&nbsp;&nbsp;/usr/bin/perl /usr/home/pro/scripts/trans.pl &gt;/dev/null&nbsp;&nbsp;2&gt;&amp;1<br/><br/>#delete old curl cookies by yunfeng@20080627<br/>18 */1 * * * /usr/bin/find /tmp/cookie/ -type f -mmin +60 -exec rm &#123;&#125; &#92;;</div><br/><br/><br/>談到 I/O redirection ，不妨先讓我們認識一下 File Descriptor (FD) 。<br/><br/>程式的運算，在大部份情況下都是進行數據(data)的處理，<br/>這些數據從哪讀進？又，送出到哪裡呢？<br/>這就是 file descriptor (FD) 的功用了。<br/><br/>在 shell 程式中，最常使用的 FD 大概有三個，分別為：<br/>0: Standard Input (STDIN)<br/>1: Standard Output (STDOUT)<br/>2: Standard Error Output (STDERR)<br/><br/>在標準情況下，這些 FD 分別跟如下設備(device)關聯：<br/>stdin(0): keyboard<br/>stdout(1): monitor<br/>stderr(2): monitor<br/><br/>我們可以用如下下命令測試一下：<br/>$ mail -s test root<br/>this is a test mail.<br/>please skip.<br/>^d (同時按 crtl 跟 d 鍵)<br/>很明顯，mail 程式所讀進的數據，就是從 stdin 也就是 keyboard 讀進的。<br/>不過，不見得每個程式的 stdin 都跟 mail 一樣從 keyboard 讀進，<br/>因為程式作者可以從檔案參數讀進 stdin ，如：<br/>$ cat /etc/passwd<br/><br/>但，要是 cat 之後沒有檔案參數則又如何呢？<br/>哦，請您自己玩玩看囉....&nbsp;&nbsp;^_^<br/>$ cat<br/><br/>(請留意數據輸出到哪裡去了，最後別忘了按 ^d 離開...)<br/>至於 stdout 與 stderr ，嗯.<br/>讓我們繼續看 stderr 好了。<br/>事實上，stderr 沒甚麼難理解的：說穿了就是&quot;錯誤信息&quot;要往哪邊送而已...<br/>比方說，若讀進的檔案參數是不存在的，那我們在 monitor 上就看到了：<br/>$ ls no.such.file<br/>ls: no.such.file: No such file or directory<br/><br/>若，一個命令同時產生 stdout 與 stderr 呢？<br/>那還不簡單，都送到 monitor 來就好了：<br/>$ touch my.file<br/>$ ls my.file no.such.file<br/>ls: no.such.file: No such file or directory<br/>my.file<br/><br/>okay，至此，關於 FD 及其名稱、還有相關聯的設備，相信你已經沒問題了吧？<br/>那好，接下來讓我們看看如何改變這些 FD 的預設數據通道，<br/>我們可用 &lt; 來改變讀進的數據通道(stdin)，使之從指定的檔案讀進。<br/>我們可用 &gt; 來改變送出的數據通道(stdout, stderr)，使之輸出到指定的檔案。<br/><br/>比方說：<br/>$ cat &lt; my.file<br/>就是從 my.file 讀進數據<br/>$ mail -s test root &lt; /etc/passwd<br/>則是從 /etc/passwd 讀進...<br/>這樣一來，stdin 將不再是從 keyboard 讀進，而是從檔案讀進了...<br/>嚴格來說，&lt; 符號之前需要指定一個 FD 的(之間不能有空白)，<br/>但因為 0 是 &lt; 的預設值，因此 &lt; 與 0&lt; 是一樣的﹗<br/><br/>okay，這個好理解吧？<br/>那，要是用兩個 &lt;&lt; 又是啥呢？<br/>這是所謂的 HERE Document ，它可以讓我們輸入一段文本，直到讀到 &lt;&lt; 後指定的字串。<br/>比方說：<br/>$ cat &lt;&lt;FINISH<br/>first line here<br/>second line there<br/>third line nowhere<br/>FINISH<br/>這樣的話，cat 會讀進 3 行句子，而無需從 keyboard 讀進數據且要等 ^d 結束輸入。<br/>當你搞懂了 0&lt; 原來就是改變 stdin 的數據輸入通道之後，相信要理解如下兩個 redirection 就不難了：<br/>* 1&gt;<br/>* 2&gt;<br/>前者是改變 stdout 的數據輸出通道，後者是改變 stderr 的數據輸出通道。<br/>兩者都是將原本要送出到 monitor 的數據轉向輸出到指定檔案去。<br/>由於 1 是 &gt; 的預設值，因此，1&gt; 與 &gt; 是相同的，都是改 stdout 。<br/><br/>用上次的 ls 例子來說明一下好了：<br/><br/>$ ls my.file no.such.file 1&gt;file.out<br/>ls: no.such.file: No such file or directory<br/>這樣 monitor 就只剩下 stderr 而已。因為 stdout 給寫進 file.out 去了。<br/><br/>$ ls my.file no.such.file 2&gt;file.err<br/>my.file<br/>這樣 monitor 就只剩下 stdout ，因為 stderr 寫進了 file.err 。<br/><br/>$ ls my.file no.such.file 1&gt;file.out 2&gt;file.err<br/>這樣 monitor 就啥也沒有，因為 stdout 與 stderr 都給轉到檔案去了...<br/><br/>呵~~~ 看來要理解 &gt; 一點也不難啦﹗是不？沒騙你吧？ ^_^<br/>不過，有些地方還是要注意一下的。<br/>首先，是 file locking 的問題。比方如下這個例子：<br/><br/>$ ls my.file no.such.file 1&gt;file.both 2&gt;file.both<br/>從 file system 的角度來說，單一檔案在單一時間內，只能被單一的 FD 作寫入。<br/>假如 stdout(1) 與 stderr(2) 都同時在寫入 file.both 的話，<br/>則要看它們在寫入時否碰到同時競爭的情形了，基本上是&quot;先搶先贏&quot;的原則。<br/>讓我們用周星馳式的&quot;慢鏡頭&quot;來看一下 stdout 與 stderr 同時寫入 file.out 的情形好了：<br/>* 第 1, 2, 3 秒為 stdout 寫入<br/>* 第 3, 4, 5 秒為 stderr 寫入<br/>那麼，這時候 stderr 的第 3 秒所寫的數據就丟失掉了﹗<br/>要是我們能控制 stderr 必須等 stdout 寫完再寫，或倒過來，stdout 等 stderr 寫完再寫，那問題就能解決。<br/>但從技術上，較難掌控的，尤其是 FD 在作&quot;長期性&quot;的寫入時...<br/><br/>那，如何解決呢？所謂山不轉路轉、路不轉人轉嘛，<br/>我們可以換一個思維：將 stderr 導進 stdout 或將 stdout 導進 sterr ，而不是大家在搶同一份檔案，不就行了﹗<br/>bingo﹗就是這樣啦：<br/>* 2&gt;&amp;1 就是將 stderr 併進 stdout 作輸出<br/>* 1&gt;&amp;2 或 &gt;&amp;2 就是將 stdout 併進 stderr 作輸出<br/>於是，前面的錯誤操作可以改為：<br/>$ ls my.file no.such.file 1&gt;file.both 2&gt;&amp;1<br/>或<br/>$ ls my.file no.such.file 2&gt;file.both &gt;&amp;2<br/><br/>這樣，不就皆大歡喜了嗎？ 呵~~~ ^_^<br/><br/>不過，光解決了 locking 的問題還不夠，我們還有其他技巧需要了解的。<br/>學佛的最高境界，就是&quot;四大皆空&quot;。至於是空哪四大塊？我也不知，因為我還沒到那境界...<br/>但這個&quot;空&quot;字，卻非常值得我們返複把玩的：<br/>--- 色即是空、空即是色﹗<br/>好了，施主要是能夠領會&quot;空&quot;的禪意，那離修成正果不遠矣~~~&nbsp;&nbsp;<br/><br/>在 Linux 檔案系統裡，有個設備檔位於 /dev/null 。<br/>許多人都問過我那是甚麼玩意兒？我跟你說好了：那就是&quot;空&quot;啦﹗<br/>沒錯﹗空空如也的空就是 null 了.... 請問施主是否忽然有所頓誤了呢？然則恭喜了~~~ ^_^<br/><br/>這個 null 在 I/O Redirection 中可有用得很呢：<br/>* 若將 FD1 跟 FD2 轉到 /dev/null 去，就可將 stdout 與 stderr 弄不見掉。<br/>* 若將 FD0 接到 /dev/null 來，那就是讀進 nothing 。<br/>比方說，當我們在執行一個程式時，畫面會同時送出 stdout 跟 stderr ，<br/>假如你不想看到 stderr (也不想存到檔案去)，那可以：<br/>$ ls my.file no.such.file 2&gt;/dev/null<br/>my.file<br/><br/>若要相反：只想看到 stderr 呢？還不簡單﹗將 stdout 弄到 null 就行：<br/>$ ls my.file no.such.file &gt;/dev/null<br/>ls: no.such.file: No such file or directory<br/><br/>那接下來，假如單純只跑程式，不想看到任何輸出結果呢？<br/>哦，這裡留了一手上次節目沒講的法子，專門贈予有緣人﹗... ^_^<br/>除了用 &gt;/dev/null 2&gt;&amp;1 之外，你還可以如此：<br/>$ ls my.file no.such.file &amp;&gt;/dev/null<br/>(提示：將 &amp;&gt; 換成 &gt;&amp; 也行啦~~! )<br/><br/>okay？講完佛，接下來，再讓我們看看如下情況：<br/>$ echo &quot;1&quot; &gt; file.out<br/>$ cat file.out<br/>1<br/>$ echo &quot;2&quot; &gt; file.out<br/>$ cat file.out<br/>2<br/>看來，我們在重導 stdout 或 stderr 進一份檔案時，似乎永遠只獲得最後一次導入的結果。<br/>那，之前的內容呢？<br/>呵~~~ 要解決這個問提很簡單啦，將 &gt; 換成 &gt;&gt; 就好：<br/>$ echo &quot;3&quot; &gt;&gt; file.out<br/>$ cat file.out<br/>2<br/>3<br/>如此一來，被重導的目標檔案之內容並不會失去，而新的內容則一直增加在最後面去。<br/>easy ？ 呵 ...&nbsp;&nbsp;^_^<br/><br/>但，只要你再一次用回單一的 &gt; 來重導的話，那麼，舊的內容還是會被&quot;洗&quot;掉的﹗<br/>這時，你要如何避免呢？<br/>----備份﹗ yes ，我聽到了﹗不過.... 還有更好的嗎？<br/>既然與施主這麼有緣份，老納就送你一個錦囊妙法吧：<br/>$ set -o noclobber<br/>$ echo &quot;4&quot; &gt; file.out<br/>-bash: file: cannot overwrite existing file<br/><br/>那，要如何取消這個&quot;限制&quot;呢？<br/>哦，將 set -o 換成 set +o 就行：<br/>$ set +o noclobber<br/>$ echo &quot;5&quot; &gt; file.out<br/>$ cat file.out<br/>5<br/><br/>再問：那... 有辦法不取消而又&quot;臨時&quot;蓋寫目標檔案嗎？<br/>哦，佛曰：不可告也﹗<br/>啊~~~ 開玩笑的、開玩笑的啦~~~&nbsp;&nbsp;^_^&nbsp;&nbsp;唉，早就料到人心是不足的了﹗<br/>$ set -o noclobber<br/>$ echo &quot;6&quot; &gt;&#124; file.out<br/>$ cat file.out<br/>6<br/>留意到沒有：在 &gt; 後面再加個&quot; &#124; &quot;就好(注意： &gt; 與 &#124; 之間不能有空白哦)....<br/><br/>呼.... (深呼吸吐納一下吧)~~~&nbsp;&nbsp;^_^<br/>再來還有一個難題要你去參透的呢：<br/>$ echo &quot;some text here&quot; &gt; file<br/>$ cat &lt; file<br/>some text here<br/>$ cat &lt; file &gt; file.bak<br/>$ cat &lt; file.bak<br/>some text here<br/>$ cat &lt; file &gt; file<br/>$ cat &lt; file<br/><br/>嗯？﹗注意到沒有？﹗﹗<br/>---- 怎麼最後那個 cat 命令看到的 file 竟是空的？﹗<br/>why? why? why?<br/>前面提到：$ cat &lt; file &gt; file 之後原本有內容的檔案結果卻被洗掉了﹗<br/>要理解這一現像其實不難，這只是 priority 的問題而已：<br/>* 在 IO Redirection 中，stdout 與 stderr 的管道會先準備好，才會從 stdin 讀進資料。<br/>也就是說，在上例中，&gt; file 會先將 file 清空，然後才讀進 &lt; file ，<br/>但這時候檔案已經被清空了，因此就變成讀不進任何資料了...<br/><br/>哦~~~ 原來如此~~~~&nbsp;&nbsp;^_^<br/>那... 如下兩例又如何呢？<br/>$ cat &lt;&gt; file<br/>$ cat &lt; file &gt;&gt; file<br/><br/>嗯... 同學們，這兩個答案就當練習題囉，下節課之前請交作業﹗<br/><br/>好了，I/O Redirection 也快講完了，sorry，因為我也只知道這麼多而已啦~~~ 嘻~~&nbsp;&nbsp;^_^<br/>不過，還有一樣東東是一定要講的，各位觀眾(請自行配樂~!#@!$%) ：<br/>---- 就是 pipe line 也﹗<br/><br/>談到 pipe line ，我相信不少人都不會陌生：<br/>我們在很多 command line 上常看到的&quot; &#124; &quot;符號就是&nbsp;&nbsp;pipe line 了。<br/>不過，究竟 pipe line 是甚麼東東呢？<br/>別急別急... 先查一下英漢字典，看看 pipe 是甚麼意思？<br/>沒錯﹗它就是&quot;水管&quot;的意思...<br/>那麼，你能想像一下水管是怎麼一根接著一根的嗎？<br/>又，每根水管之間的 input 跟 output 又如何呢？<br/>嗯？？<br/>靈光一閃：原來 pipe line 的 I/O 跟水管的 I/O 是一模一樣的：<br/>* 上一個命令的 stdout 接到下一個命令的 stdin 去了﹗<br/>的確如此... 不管在 command line 上你使用了多少個 pipe line ，<br/>前後兩個 command 的 I/O 都是彼此連接的﹗(恭喜：你終於開竅了﹗&nbsp;&nbsp;^_^ )<br/><br/>不過... 然而... 但是...&nbsp;&nbsp;... stderr 呢？<br/>好問題﹗不過也容易理解：<br/>* 若水管漏水怎麼辦？<br/>也就是說：在 pipe line 之間，前一個命令的 stderr 是不會接進下一命令的 stdin 的，<br/>其輸出，若不用 2&gt; 導到 file 去的話，它還是送到監視器上面來﹗<br/>這點請你在 pipe line 運用上務必要注意的。<br/><br/>那，或許你又會問：<br/>* 有辦法將 stderr 也餵進下一個命令的 stdin 去嗎？<br/>(貪得無厭的家夥﹗)<br/>方法當然是有，而且你早已學過了﹗&nbsp;&nbsp;^_^<br/>我提示一下就好：<br/>* 請問你如何將 stderr 合併進 stdout 一同輸出呢？<br/>若你答不出來，下課之後再來問我吧... (如果你臉皮真夠厚的話...)<br/><br/>或許，你仍意尤未盡﹗或許，你曾經碰到過下面的問題：<br/>* 在 cm1 &#124; cm2 &#124; cm3&nbsp;&nbsp;... 這段 pipe line 中，若要將 cm2 的結果存到某一檔案呢？<br/><br/>若你寫成 cm1 &#124;&nbsp;&nbsp;cm2 &gt; file &#124; cm3 的話，<br/>那你肯定會發現 cm3 的 stdin 是空的﹗(當然啦，你都將水管接到別的水池了﹗)<br/>聰明的你或許會如此解決：<br/>cm1 &#124; cm2 &gt; file ; cm3 &lt; file<br/>是的，你的確可以這樣做，但最大的壞處是：這樣一來，file I/O 會變雙倍﹗<br/>在 command 執行的整個過程中，file I/O 是最常見的最大效能殺手。<br/>凡是有經驗的 shell 操作者，都會盡量避免或降低 file I/O 的頻率。<br/><br/>那，上面問題還有更好方法嗎？<br/>有的，那就是 tee 命令了。<br/>* 所謂 tee 命令是在不影響原本 I/O 的情況下，將 stdout 複製一份到檔案去。<br/>因此，上面的命令行可以如此打：<br/>cm1 &#124; cm2 &#124; tee file &#124; cm3<br/>在預設上，tee 會改寫目標檔案，若你要改為增加內容的話，那可用 -a 參數達成。<br/><br/>基本上，pipe line 的應用在 shell 操作上是非常廣泛的，尤其是在 text filtering 方面，<br/>凡舉 cat, more, head, tail, wc, expand, tr, grep, sed, awk, ... 等等文字處理工具，<br/>搭配起 pipe line 來使用，你會驚覺 command line 原來是活得如此精彩的﹗<br/>常讓人有&quot;眾裡尋他千百度，驀然回首，那人卻在燈火闌珊處﹗&quot;之感...&nbsp;&nbsp;^_^<br/><br/>....<br/><br/>好了，關於 I/O Redirection 的介紹就到此告一段落。<br/>若日後有空的話，再為大家介紹其它在 shell 上好玩的東西﹗bye...&nbsp;&nbsp;^_^<br/>俺谈谈：&gt;是覆盖，所以先将文件清空<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&gt;&gt;是追加，所以先找文件的尾巴哦，在尾巴后面写<br/>不知道明白否
]]>
</description>
</item><item>
<link>http://www.jackxiang.com/post//#blogcomment</link>
<title><![CDATA[[评论] [实践OK]linux shell 中21含义的指针理解,如何把错误定向到空文件？]]></title> 
<author> &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> 
<guid>http://www.jackxiang.com/post//#blogcomment</guid> 
<description>
<![CDATA[ 
	
]]>
</description>
</item>
</channel>
</rss>