15.1 了解輸入和輸出
現在知道兩種顯示腳本輸出的方法
1)在顯示器螢幕上顯示
2)将輸出檔案重定向到檔案中
15.1.1 标準檔案描述符
Linux系統将每個對象當做檔案處理。這包括輸入和數出程序。
Linux用檔案描述符來辨別每個檔案對象。
它是一個非負整數,可以唯一辨別會話中打開的檔案。
每個程序一次最多可以有九個檔案描述符
bash shell保留的前3個檔案描述符(0、 1、 2)
1.STDIN 标準輸入(0)
STDIN檔案代表shell的标準輸入。
對終端界面來說,标準輸入是鍵盤。
shell從STDIN檔案描述符對應的鍵盤獲得輸入,在使用者輸入時處理每個字元
在使用輸入重定向符号( < )時,Linux會用重定向指定的檔案來替換标準輸入檔案描述符。它會讀取檔案并提取資料,就如同它是鍵盤上鍵入的。
2.STDOUT 标準輸出(1)
STDOUT檔案描述符代表shell的标準輸出。
對終端界面來說,标準輸出是終端顯示器。shell的所有輸出會被定向到标準輸出中。
也可以通過輸出重定向( > )來改變輸出。通過輸出重定向符号,可以将本來顯示在顯示器上的輸出重定向到指定的檔案。
>> 表示追加到檔案
注意:用了輸出重定向,shell并未将錯誤消息重定向到輸出重定向檔案中。錯誤消息仍會顯示在顯示器中。
3.STDERR 标準錯誤(2)
STDERR檔案描述符來處理錯誤消息。
shell或shell中運作的程式和腳本出錯時生成的錯誤消息都會發送到這個位置。
預設情況下STROUT和STDERR指向同樣的地方(顯示器)。但是STDERR不會随着STDOUT重定向而發生改變。
15.1.2 重定向錯誤
1.隻重定向錯誤
将該檔案描述符值(2)放在重定向符号(>)前面,必須挨着,不能有空格。
比如,檢視一個不存在的檔案:
$ls –al 2> log.txt
這種方法隻會重定向錯誤消息,普通輸出不會被重定向。
2. 重定向錯誤和資料
需要用兩個重定向符号,需要在符号前面放上待重定向資料所對應的檔案描述符,然後指定用于儲存資料的輸出檔案。
例如:
$ls -al test1 test2 test3 badfile 2> ErrLog.txt 1> DataLog.txt
表示将錯誤資訊重定向到ErrLog.txt,正常輸出重定向到DataLog.txt。
這樣錯誤資訊和正常輸出就分開在兩檔案了。
$ls -al test1 test2 test3 badfile &> AllLog.txt
這樣表示将STDOUT和STDERR重定向到同一個檔案AllLog.txt中了。
bash shell自動賦予了錯誤消息更高的優先級,這樣可以集中浏覽錯誤資訊了。
15.2 在腳本中重定向輸出
有兩種方法:
1)臨時重定向行輸出
2)永久重定向腳本中的所有指令
15.2.1 臨時重定向
可以單獨将一行重定向到STDERR。
比如:
echo “this is error msg” >&2
echo “this is normal msg”
正常運作不會看出什麼,但是假如運作時重定向了STDERR就有意思了。
$./test 2> Error.txt
就可以看到第一行輸出到了 Error.txt。而正常輸出還是在螢幕上。
15.2.2 永久重定向
如果有大量資料需要重定向,那麼就會比較麻煩。
新方法:用exec指令告訴shell在腳本執行期間重定向某個特定檔案描述符
直接上例子:
1 #!/bin/bash
2 echo "this is error msg step1" >&2
3 echo "this is normal msg step1"
4 # 上面沒有重定向,是以還是在螢幕輸出。下面才開始重定向到需要的檔案中
5 exec 1>test2log.txt
6 exec 2>test2Error.txt
7 echo "this is error msg step2" >&2
8 echo "this is normal msg step2"
這樣一旦重定向了就很難改回去了。
15.3 在腳本中重定向輸入
exec 指令允許你将STDIN重定向到Linux系統上的檔案中。
例子:檢視test2中的資料
1 #!/bin/bash
2 exec 0< test2 # 輸入重定向到test2中
3 echo "test2:"
4 count=1
5 while read line
6 do
7 echo " $line"
8 count=$[ $count + 1 ]
9 done
15.4 建立自己的重定向
之前說一個程序最多可以與9個打開的檔案描述符。其他6個(3 ~ 8)的檔案描述符均可用作輸入或輸出重定向。
可以将這些檔案描述符中的任意一個配置設定給檔案。
15.4.1 建立輸出檔案描述符
用exec指令給輸出配置設定檔案描述符。
和标準的檔案描述符一樣,一旦将另一個檔案描述符配置設定給了一個檔案,這個重定向就會一直有效,直到你重新配置設定。
例子:
2 exec 3>test4log.txt # exec 3>>test4log.txt 這個是将輸出追加到現有檔案
3 echo "This is Normal msg"
4 echo "This is fd:3 msg" >&3
15.4.2 重定向檔案描述符
現在介紹怎麼恢複已重定向的檔案描述符。
可以配置設定另外一個檔案描述符給标準檔案描述符,反之亦然。
可以将STDOUT重定向到另外一個檔案描述符,然後再利用該檔案描述符重定向回STDOUT
2 # storing STDOUT, then coming back to it
3 exec 3>&1 # 3重定向到STDOUT。意味着給3的資料都将出現再顯示器上
4 exec 1>test5log.txt # 将STDOUT重定向到檔案。但是3仍然指向STDOUT原來的位置,也就是顯示器。這時給3發會顯示在顯示器中。給STDOUT發會顯示在檔案中
5 echo "This should store in the output file"
6 echo "alone with this line."
7
8 exec 1>&3 # 将STDOUT重定向到3的目前位置(也就是顯示器)
9 echo "now things should be back to normal"
15.4.3 建立輸入檔案描述符
跟上面類似,先将STDIN儲存到另外一個檔案描述符,然後讀取完檔案在将STDIN恢複
2 exec 6<&0 # 6先儲存STDIN的位置
3 exec 0<test5 # 将STDIN重定向到 test5
8 count=$[ $count +1 ]
10
11 exec 0<&6 # 讀取完成後将STDIN重定向到檔案描述符6,進而恢複之前的位置
12 read -p "Are you done now?" answer
13 case $answer in
14 Y|y) echo "GoodBye!!!";;
15 N|n) echo "Sorry, this is the end";;
16 *) echo "Error End";;
17 esac
15.4.4 建立讀寫檔案描述符
可以打開單個檔案描述符作為輸入和輸出。可以利用同一個檔案描述符對同一個檔案進行讀寫。
用起來要小心:由于是對同一個檔案進行資料讀寫,shell會維護一個内部指針,指明在檔案中的目前位置。任何讀或寫都是從檔案指針上次的位置開始。
2 exec 3<> testfile
3 read line <&3
4 echo "Read:$line" #注意這裡寫是從檔案指針上次的位置開始,也就是讀了一行之後的位置
5 echo "Write: This is test line" >&3
15.4.5 關閉檔案描述符
如果你建立了新的輸入或輸出檔案描述符,shell會在腳本退出時自動關閉它們。
但是某些時候還是要自己去關閉。
如何關閉: 将要關閉的檔案描述符重定向到特殊符号 &-
一旦關閉後,就不能在腳本中向他寫入資料,否則shell會産生錯誤資訊。
2 # close fd test
3 exec 3>test8log.txt
4 echo "This is normal to fd:3" >&3
5 exec 3>&-
6 echo "after close write:his is normal to fd:3" >&3 # 關閉後再往裡面寫會出錯
8 exec 3>test8log.txt # 這裡相當于重新打開了
9 echo "This is bad normal to fd:3" >&3 # 會覆寫原來的
15.5 列出打開的檔案描述符
lsof指令會列出整個linux系統打開的所有的檔案描述符。會産生大量輸出。
還可以接選項和參數:
-p 後面接要檢視的程序。 $$ 表示目前程序
-d 後面指定要顯示的檔案描述符編号。
2 exec 3> testfile
3 lsof -a -p $$ -d 0,1,2,3,4
15.6 阻止指令輸出
有時不想顯示腳本的輸出。可以将輸出重定向到一個叫做null檔案的特殊檔案中去。
$ls –al > /dev/null
還可以這樣清空日志檔案
$ cat /dev/null > TestLog.txt
15.7 建立臨時檔案
linux使用/tmp目錄來存放不需要永久保留的檔案。大部分linux發行版配置了系統在啟動時自動删除/tmp目錄下的所有檔案。
系統上的任何使用者賬戶都有權限在讀寫/tmp目錄中的檔案。
mktemp可以在/tmp目錄中建立一個唯一的臨時檔案。一旦建立了檔案,你就在腳本中有了完整的讀寫權限,别人無法通路它。
15.7.1 建立本地臨時檔案
隻需要指定一個檔案名模闆就行了,在檔案末尾加上6個X。
$mktemp testing.XXXXXX
注意:這裡一定要有大寫的X。這裡的X有點通配符的意思。還可以寫不是X的
mktemp指令的輸出是它所建立的檔案的名字。在腳本中儲存起來,就能在後面的腳本裡引用了。
2 # create and using temp file
3 tempfile=$(mktemp test10.XXXXXX)
4 echo "tempfile = $tempfile"
5 exec 3>$tempfile
6 echo "This script writes to tmp file $tempfile"
7 echo "this is first line" >&3
8 echo "this is second line" >&3
9 echo "this is third line" >&3
10 exec 3>&-
11
12 echo "Now delete file $tempfile"
13 rm -f $tempfile > /dev/null
15.7.2 在/tmp目錄建立臨時檔案
-t 選項會強制mktemp在系統的臨時目錄來建立該檔案。
這個時候傳回用來建立臨時檔案的全路徑,而不是隻有檔案名。
就上面的例子加上 –t就好了。
。。。
tempfile=$(mktemp -t test10.XXXXXX)
15.7.3 建立臨時目錄
-d選項用來建立臨時目錄。這樣就能用改目錄進行任何需要的操作了。
2 # create and using temp dir
3 tempdir=$(mktemp -d test12dir.12XXXX)
4 cd $tempdir
5 echo This in Dir:$(pwd)
6 tempfile=$(mktemp test12.XXXXXX)
7 echo "tempfile = $tempfile"
8 exec 3>$tempfile
9 echo "This script writes to tmp file $tempfile"
10 echo "this is first line" >&3
11 echo "this is second line" >&3
12 echo "this is third line" >&3
15.8 記錄消息
将輸出同時發送到顯示器和日志檔案,需要特殊指令tee就可以了。
tee指令相當于管道第一個T型接頭。它将STDIN過來的資料同時發往兩處,一處是STDOUT,一處是指定的檔案。
$date | tee log.txt
$date | tee –a log.txt # 這個是将資料追加到檔案中
2 # tee test
3 echo "This is 1 msg" | tee test13log.txt
4 echo "This is 2 msg" | tee -a test13log.txt
5 echo "This is 3 msg" | tee -a test13log.txt
15.9 執行個體
檔案重定向常見于腳本需要讀入檔案和輸出檔案時。
需求:把資料資料放入電子表格中(.csv檔案),讀取檔案,建立INSERT語句。
2 outfile='members.sql'
3 IFS=,
4 while read name age sex num
5 do
6 cat >> $outfile << EOF
7 insert into members (name, age, sex, num) values('$name', '$age', '$sex', '$num');
8 EOF
9 done <${1}
1)${1}代表第一個指令行參數。它指明了待讀取資料的檔案。
2)read會用IFS字元解析讀入的文本,我們在這裡将IFS指定為逗号。
cat >> $outfile << EOF // 這一段還是不大了解
這個包含一個輸出追加重定向(>>)和一個輸入追加重定向(<<)。
>> 将cat指令的輸出追加到由$outfile變量指定的檔案中。
cat指令的輸入不在取自标準輸入,而是被重定向到腳本中存儲的資料。
EOF符号标記了追加到檔案中的資料的起止。
輸入檔案 + 運作 + 結果:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcuEDMmRTZxY2Y4gzYmdTOmVGN2EGN2YTYyYGZhFDNiZzMfdWbp9CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.png)
說明:
特殊重定向(here document):
command << delimiter
document
delimiter
作用是将兩個delimiter之間的内容(document)作為輸入傳給command
注意:結尾的delimiter一定要頂格寫,不能有空格。
(1)
(2)
6 cat << EOF
黃色高亮部分作為輸入傳給cat。(1)重定向到outfile去了,(2)仍然是标準輸出(螢幕)