《Linux命令行與shell腳本編程大全》第十五章 呈現數據
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命令給輸出分配文件描述符。
和標準的文件描述符一樣,一旦將另一個文件描述符分配給了一個文件,這個重定向就會一直有效,直到你重新分配。
例子:
1 #!/bin/bash
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
例子:
1 #!/bin/bash
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恢復
例子:
1 #!/bin/bash
2 exec 6<&0 # 6先保存STDIN的位置
3 exec 0<test5 # 將STDIN重定向到 test5
4 count=1
5 while read line
6 do
7 echo " $line"
8 count=$[ $count +1 ]
9 done
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會維護一個內部指針,指明在文件中的當前位置。任何讀或寫都是從文件指針上次的位置開始。
例子:
1 #!/bin/bash
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會產生錯誤信息。
例子:
1 #!/bin/bash
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 # 關閉後再往裏面寫會出錯
7
8 exec 3>test8log.txt # 這裏相當於重新打開了
9 echo "This is bad normal to fd:3" >&3 # 會覆蓋原來的
15.5 列出打開的文件描述符
lsof命令會列出整個linux系統打開的所有的文件描述符。會產生大量輸出。
還可以接選項和參數:
-p 後面接要查看的進程。 $$ 表示當前進程
-d 後面指定要顯示的文件描述符編號。
例子:
1 #!/bin/bash
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命令的輸出是它所創建的文件的名字。在腳本中保存起來,就能在後面的腳本裏引用了。
例子:
1 #!/bin/bash
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選項用來創建臨時目錄。這樣就能用改目錄進行任何需要的操作了。
例子:
1 #!/bin/bash
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 # 這個是將數據追加到文件中
例子:
1 #!/bin/bash
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語句。
例子:
1 #!/bin/bash
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符號標記了追加到文件中的數據的起止。
輸入文件 + 運行 + 結果:
說明:
特殊重定向(here document):
command << delimiter
document
delimiter
作用是將兩個delimiter之間的內容(document)作為輸入傳給command
註意:結尾的delimiter一定要頂格寫,不能有空格。
(1)
6 cat >> $outfile << EOF
7 insert into members (name, age, sex, num) values(‘$name‘, ‘$age‘, ‘$sex‘, ‘$num‘);
8 EOF
(2)
6 cat << EOF
7 insert into members (name, age, sex, num) values(‘$name‘, ‘$age‘, ‘$sex‘, ‘$num‘);
8 EOF
黃色高亮部分作為輸入傳給cat。(1)重定向到outfile去了,(2)仍然是標準輸出(屏幕)
《Linux命令行與shell腳本編程大全》第十五章 呈現數據