# 練習 7:Bash:重定向,`stdin`,`stdout`,`stderr`,`<`,`>`,`>>`,`|`,`tee`,`pv`
> 原文:[Exercise 7. Bash: redirection, stdin, stdout, stderr, <, >, >>, |, tee, pv](https://archive.fo/hZqGb)
> 譯者:[飛龍](https://github.com/wizardforcel)
> 協議:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
> 自豪地采用[谷歌翻譯](https://translate.google.cn/)
在 Linux 中,一切都只是文件。這意味著,對于控制臺程序:
+ 鍵盤表示為一個文件,Bash 從中讀取你的輸入。
+ 顯示器表示為一個文件,Bash向輸出寫入它。
讓我們假設,你有一個程序可以計算文件中的行。你可以通過鍵入`wc -l`來調用它。現在嘗試一下 沒有發生什么事吧?它只是卡在那里。錯了,它正在等待你的輸入。這是它的工作原理:
```
line_counter = 0
while end of file is not reached
read a line
add 1 to line_counter
print line_counter
```
所以`wc`目前從`/dev/tty`讀取行,這在當前上下文中是你的鍵盤。每次你按下回車,`wc`都會獲取一行。任意鍵入幾行,然后按`CTRL + D`,這將為`wc`產生`EOF`字符,使其明白達到[文件末尾](http://en.wikipedia.org/wiki/End-of-file)。現在它會告訴你你輸入了多少行。
但是如果你想計算現有文件中的行呢?那么,這就需要重定向 了!為了在其輸入上提供現有文件,請鍵入以下內容:`wc -l < .bash_history`。你看,它有作用!真的是那么簡單 是一種機制,允許你告訴程序,將其來自鍵入輸入和/或到顯示器的輸出,重定向到另一個文件。為此,你可以使用這些特殊命令,然后啟動程序:
+ `<` - 用文件替換標準輸入(例如鍵盤)。
+ `>` - 用文件替換標準輸出(例如顯示器)。警告!此命令將覆蓋 你的指定文件的內容,因此請小心。
+ `>>` - 與上面相同,但不是覆蓋 文件,而是寫入到它的末尾,保存在該文件中已存在的信息。小心不要混淆兩者。
+ `|` - 從一個程序獲取輸出,并將其連接到另一個程序。這將在下一個練習中詳細闡述。
現在,你將學習如何將程序的輸入和輸出重定向到文件或其他程序。
## 這樣做
```
1: sudo aptitude install pv
2: read foo < /dev/tty
3: Hello World!
4: echo $foo > foo.out
5: cat foo.out
6: echo $foo >> foo.out
7: cat foo.out
8: echo > foo.out
9: cat foo.out
10: ls -al | grep foo
11: ls -al | tee ls.out
12: dd if=/dev/zero of=~/test.img bs=1M count=10
13: pv ~/test.img | dd if=/dev/stdin of=/dev/null bs=1
14: rm -v foo.out
15: rm -v test.img
```
## 你應該看到什么
```
user1@vm1:~$ sudo aptitude install pv
The following NEW packages will be installed:
pv
0 packages upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 0 B/28.9 kB of archives. After unpacking 143 kB will be used.
Selecting previously deselected package pv.
(Reading database ... 39657 files and directories currently installed.)
Unpacking pv (from .../archives/pv_1.1.4-1_amd64.deb) ...
Processing triggers for man-db ...
Setting up pv (1.1.4-1) ...
user1@vm1:~$ read foo < /dev/tty
Hello World!
user1@vm1:~$ echo $foo > foo.out
user1@vm1:~$ cat foo.out
Hello World!
user1@vm1:~$ echo $foo >> foo.out
user1@vm1:~$ cat foo.out
Hello World!
Hello World!
user1@vm1:~$ echo > foo.out
user1@vm1:~$ cat foo.out
user1@vm1:~$ ls -al | grep foo
-rw-r--r-- 1 user1 user1 1 Jun 15 20:03 foo.out
user1@vm1:~$ ls -al | tee ls.out
total 44
drwxr-xr-x 2 user1 user1 4096 Jun 15 20:01 .
drwxr-xr-x 3 root root 4096 Jun 6 21:49 ..
-rw------- 1 user1 user1 4865 Jun 15 19:34 .bash_history
-rw-r--r-- 1 user1 user1 220 Jun 6 21:48 .bash_logout
-rw-r--r-- 1 user1 user1 3184 Jun 14 12:24 .bashrc
-rw-r--r-- 1 user1 user1 1 Jun 15 20:03 foo.out
-rw------- 1 user1 user1 50 Jun 15 18:41 .lesshst
-rw-r--r-- 1 user1 user1 0 Jun 15 20:03 ls.out
-rw-r--r-- 1 user1 user1 697 Jun 7 12:25 .profile
-rw-r--r-- 1 user1 user1 741 Jun 7 12:19 .profile.bak
-rw-r--r-- 1 user1 user1 741 Jun 7 13:12 .profile.bak1
-rw-r--r-- 1 user1 user1 0 Jun 15 20:02 test.img
user1@vm1:~$ dd if=/dev/zero of=~/test.img bs=1M count=10
10+0 records in
10+0 records out
10485760 bytes (10 MB) copied, 0.0130061 s, 806 MB/s
user1@vm1:~$ pv ~/test.img | dd if=/dev/stdin of=/dev/null bs=1
10MB 0:00:03 [3.24MB/s] [=================================================================================>] 100%
10485760+0 records in
10485760+0 records out
10485760 bytes (10 MB) copied, 3.10446 s, 3.4 MB/s
user1@vm1:~$ rm -v foo.out
removed `foo.out'
user1@vm1:~$ rm -v test.img
removed `test.img'
user1@vm1:~$
```
## 解釋
1. 在你的系統上安裝`pv`(管道查看器)程序,稍后你需要它。
2. 將你的輸入讀取到變量`foo`。這是可能的,因為顯示器和鍵盤實際上是系統的電傳打字機。是的,[那個](http://www.google.ru/search?rlz=1C1CHKZ_enRU438RU438&sugexp=chrome,mod%3D11&q=unix+filter&um=1&ie=UTF-8&hl=en&tbm=isch&source=og&sa=N&tab=wi&ei=QWDbT7LILsTi4QTJxNXWCg&biw=1116&bih=875&sei=Q2DbT93XOLLS4QTmst2ACg%23um=1&hl=en&newwindow=1&rlz=1C1CHKZ_enRU438RU438&tbm=isch&sa=1&q=teletype&oq=teletype&aq=f&aqi=g10&aql=&gs_l=img.3..0l10.455489.456448.4.456736.8.6.0.2.2.0.144.567.4j2.6.0...0.0.Qa6W2PHvUWw&pbx=1&bav=on.2,or.r_gc.r_pw.r_qf.,cf.osb&fp=e87c07212bd9e2a6&biw=1116&bih=875)電傳打字機!在線閱讀更多關于`tty`的東西。
3. 這是你輸入的東西。
4. 將`foo`變量的內容重定向到`foo.out`文件,在進程中創建文件或覆蓋現有文件,而不會警告刪除所有內容!
5. 打印出`foo.out`的內容。
6. 將`foo`變量的內容重定向到`foo.out`文件,在進程中創建文件或附加 到現有文件。這是安全的,但不要混淆這兩者,否則你會有巨大的悲劇。
7. 再次打印出`foo.out`內容。
8. 將空內容重定向到`foo.out`,在進程中清空文件。
9. 顯示文件確實是空的。
0. 列出你的目錄并將其通過管道輸出到`grep`。它的原理是,獲取所有`ls -al`的輸出,并將其扔給`grep`。這又稱為管道。
1. 將你的目錄列出到屏幕上,并寫入`ls.out`。很有用!
2. 創建大小為 10 兆字節的清零文件。現在不要糾結它如何工作。
3. 將這個文件讀取到`/dev/null`,這是你系統中終極的垃圾桶,什么都沒有。寫入它的一切都會消失。請注意,`pv`會向你展示讀取文件的進程,如果你嘗試使用其他命令讀取它,你就不會知道它需要多長時間來完成。
4. 刪除`foo.out`。記得自己清理一下。
5. 刪除`test.img`。
## 附加題
+ 閱讀 [stackoverflow](http://stackoverflow.com/questions/5802879/difference-between-pipelining-and-redirection-in-linux) 上的管道和重定向,再次閱讀 [stackoverflow](http://stackoverflow.com/questions/19122/bash-pipe-handling?rq=1) 和 [Greg 的 Wiki](http://mywiki.wooledge.org/BashFAQ/024),這是非常有用的資源,記住它。
+ 打開 bash 的`man`頁面,向下滾動到 REDIRECTION 部分并閱讀它。
+ 閱讀`man pv`和`man tee`的描述。
- 笨辦法學 Linux 中文版
- 練習 0:起步
- 練習 1:文本編輯器,vim
- 練習 2:文本瀏覽器,少即是多
- 練習 3:Bash:Shell、.profile、.bashrc、.bash_history
- 練習 4:Bash:處理文件,pwd,ls,cp,mv,rm,touch
- 練習 5:Bash:環境變量,env,set,export
- 練習 6:Bash:語言設置,LANG,locale,dpkg-reconfigure locales
- 練習 7:Bash:重定向,stdin,stdout,stderr,<,>,>>,|,tee,pv
- 練習 8:更多的重定向和過濾:head,tail,awk,grep,sed
- 練習 9:Bash:任務控制,jobs,fg
- 練習 10:Bash:程序退出代碼(返回狀態)
- 練習 11:總結
- 練習 12:文檔:man,info
- 練習 13:文檔:Google
- 練習 14:包管理:Debian 包管理工具aptitude
- 練習 15:系統啟動:運行級別,/etc/init.d,rcconf,update-rc.d
- 練習 16:處理進程,ps,kill
- 練習 17:任務調度:cron,at
- 練習 18:日志:/var/log,rsyslog,logger
- 練習 19:文件系統:掛載,mount,/etc/fstab
- 練習 20:文件系統:修改和創建文件系統,tune2fs,mkfs
- 練習 21:文件系統:修改根目錄,chroot
- 練習 22:文件系統:移動數據,tar,dd
- 練習 23:文件系統:權限,chown,chmod,umask
- 練習 24:接口配置,ifconfig,netstat,iproute2,ss,route
- 練習 25:網絡:配置文件,/etc/network/interfaces
- 練習 26:網絡:封包過濾配置,iptables
- 練習 27:安全 Shell,ssh,sshd,scp
- 練習 28:性能:獲取性能情況,uptime,free,top
- 練習 29:內核:內核消息,dmesg
- 練習 30:打磨、洗練、重復:總復習
- 下一步做什么
- Debian 手動安裝