I/O重定向
通過這個工具,可以重定向命令的輸入輸出,命令的輸入來自文件,而輸出也存到文件。 也可以把多個命令連接起來組成一個強大的命令管道。
cat — 連接文件
sort — 排序文本行
uniq — 報道或省略重複行
grep — 打印匹配行
wc — 打印文件中換行符,字,和字節個數
head — 輸出文件第一部分
tail — 輸出文件最後一部分
tee — 從標準輸入讀取數據,並同時寫到標準輸出和文件
標準輸入、輸出和錯誤
標準輸出通常由兩種類型組成:
1、程序運行結果(程序要完成的功能)
2、程序運行狀態和錯誤信息(程序進展)
“everything is a file”,程序實際是把運行的結果輸送到一個叫做標準輸出的特殊文件(通常用stdout表示),把狀態信息輸送到叫做標準錯誤的文件(stderr)。默認情況下,標準輸出和標準錯誤都連接到屏幕,而不是保存到磁盤。另一方面,很多程序從叫做標準輸入的文件中(stdin)得到輸入,標準輸入默認連接到鍵盤。I/O重定向允許我們更改輸入來源和輸出地點。
標準輸出重定向
可以使用 “>” 重定向符號接文件名將標準輸出輸送到屏幕以外的其他文件。注意:使用 “>” 重定向操作符來重定向輸出結果時,目標文件總是從開頭被重寫。所以每次使用重定向操作,目標文件原本的內容會被清空。
事實上,我們也可以使用重定向符號清空一個文件的內容(或者創建一個新的空文件):
> file_name
我們還可以使用 “>>” 重定向操作符把重定向內容追加到文件原來的內容後面(也可以用作創建新文件),而不是重寫文件:
echo ‘I am a boy’ >> a.txt #追加單行文本
ls -l /usr/bin >> a.txt #將ls命令的結果(標準輸出)追加到文件中
標準錯誤重定向
標準錯誤重定向沒有專用的操作符,爲了重定向標準錯誤,我們必須結合其文件描述符。一個程序可以在任一個經過編號的文件流上產生輸出。我們將前三個編號的文件流稱作標準輸入、輸出和錯誤,shell分別將其稱爲文件描述符0、1和2。即文件描述符2等同於標準錯誤,我們可以將文件描述符 “2” 與重定位符連在一起來重定向標準錯誤:
ls -l /bin/usr 2> a.txt #/bin/usr是一個不存在的目錄,因此ls命令會輸出標準錯誤
重定向標準輸出和錯誤到同一個文件
兼容舊版本shell的方法:
ls -l /bin/usr > a.txt 2>&1
首先重定向標準輸出到文件a.txt,然後 重定向文件描述符2(標準錯誤)到文件描述符1(標準輸出)使用表示法2>&1。注意重定向的順序,標準錯誤的重定向必須總是出現在標準輸出 重定向之後,要不然它不起作用。
現在的bash版本還提供第二種方法:
ls -l /bin/usr &> a.txt
我們使用一個&符號跟重定向操作符連接起來 “&>” 來重定向標準輸出和錯誤到文件 a.txt。
處理不需要的輸出
有時候我們不想要一個命令的輸出結果,例如錯誤和狀態信息。系統中有一個叫做 “/dev/null” 的特殊文件,它是一個系統設備,叫做位存儲桶,它可以接受輸入但並不對輸入做任何處理。我們可以將標準錯誤重定向到該文件中。
ls -l /bin/usr 2> /dev/null
標準輸入重定向
先介紹一個能夠用到標準輸入的命令 cat,cat命令讀取一個或多個文件,然後複製它們的內容到標準輸出。可以使用cat不分頁的顯示文件內容。
cat file_name...
cat 經常被用來顯示簡短的文本文件。因爲 cat 可以 接受不只一個文件作爲參數,所以它也可以用來把文件連接在一起。比方說我們下載了一個 大型文件,這個文件被分離成多個部分(USENET 中的多媒體文件經常以這種方式分離), 我們想把它們連起來。如果文件命名爲:
movie.mpeg.001 movie.mpeg.002 … movie.mpeg.099
我們可以使用通配符將它們連接起來,並重定向到一個文件中:
cat movie.mpeg.0* > movie.mpeg
目前爲止,和標準輸入還沒什麼關係。但是,如果我們不給cat輸入參數直接運行,它就會從標準輸入讀入數據,而標準輸入默認連接到鍵盤,所以當我們運行了不帶參的cat命令的時候,它就會等待我們輸入數據而不會顯示任何內容。
當我們從鍵盤輸入內容並按下enter鍵的時候,就可以看到屏幕上的文本行重複出現。
ldz@MSI:~$ cat
his cat is black.
his cat is black.
我們可以不斷的輸入內容,直到按下Ctrl+d告訴cat已經到達了文件末尾(EOF)。
我們可以利用cat結合重定向操作符創建簡短的文本文件或代碼文件,比如:
ldz@MSI:~$ cat > a.txt
hahahahahaha,打不過我吧。
哈哈哈哈哈,沒有辦法我就是這麼強大。
注意結束輸入的時候需要enter換行然後按下Ctrl+d,或者不換行連續使用兩次Ctrl+d。
接下來,我們使用重定向操作符 “<” 來重定向標準輸入:
cat < a.txt #將標準輸入源從鍵盤改到文件a.txt
管道線
命令從標準輸入讀取數據並輸送到標準輸出的能力被一個稱爲管道線的 shell 特性所利用。 使用管道操作符 ”|”(豎槓),一個命令的標準輸出可以通過管道送至另一個命令的標準輸入(也屬於I/O重定向):
command1 | command2
爲了說明這個操作符,這裏介紹一下less命令的另一個功能,除了瀏覽文件內容,它還可以接收標準輸入並把其輸送到標準輸出並一頁一頁地顯示。
ls -l /usr/bin | less
利用這個功能,我們可以非常方便的查看會產生標準輸出的任一命令的運行結果。
過濾器
管道線經常用來對數據完成複雜的操作。有可能會把幾個命令放在一起組成一個管道線。 通常,以這種方式使用的命令被稱爲過濾器。
比如,把目錄 /bin 和 /usr/bin 中的可執行程序放在一起並進行排序然後輸出
ls /bin /usr/bin | sort | less
因爲我們指定了兩個目錄(/bin 和 /usr/bin),ls 命令的輸出結果由兩個有序列表組成, 各自針對一個目錄。通過在管道線中包含 sort,我們改變輸出數據,從而產生一個有序列表。
uniq
uniq 命令經常和 sort 命令結合在一起使用。uniq 從標準輸入或單個文件名參數接受數據有序 列表,默認情況下,從數據列表中刪除重複行。(/bin 和 /usr/bin中有重名的程序 )
ls /bin /usr/bin | sort | uniq | less
如果想要輸出重複行的數據,可以使用uniq的-d選項
ls /bin /usr/bin | sort | uniq -d | less
wc
wc命令是用來顯示文件所包含的行數、字數(一個字被定義爲由空格或換行字符分隔的字符串)和字節數。
wc a.txt
查看有序列表中的程序個數,-l 選項表示只輸出行數
ls /bin /usr/bin | sort | uniq | wc -l
grep
grep 是個很強大的程序,用來找到文件中的匹配文本。
grep pattern file_name...
當 grep 遇到一個文件中的匹配”模式”(正則表達式),它會打印出包含這個類型的行。比如在程序列表中,找到文件名中包含單詞”zip”的所有程序:
ls /bin /usr/bin | sort | uniq | grep zip
grep 有一些方便的選項:”-i”使得 grep 在執行搜索時忽略大小寫(通常,搜索是大小寫 敏感的),”-v”選項會告訴 grep 只打印不匹配的行。
head / tail
head 命令打印文件的前十行,而 tail 命令打印文件的後十行。默認情況下,兩個命令都打印十行文本,但是可以通過”-n”選項來調整命令打印的行數。
head -n 5 a.txt
tail -n 5 a.txt
ls /usr/bin | tail -n 5
tail 有一個選項 “-f” 允許你實時地瀏覽文件。當觀察日誌文件的進展時很有用,因爲它們同時在被寫入。
tail -f logfile_name
使用”-f”選項,tail 命令持續監測這個文件,當新的內容添加到文件後,會立即出現在屏幕上。直到你輸入 Ctrl+c。
tee
從標準輸入讀取數據,並且同時輸出到標準輸出(允許數據繼續隨着管道線流動)和一個或多個文件。主要用於在某個中間處理 階段來捕捉管道線的內容。
ls /usr/bin | tee ls.txt | grep zip
字符展開
再介紹一個新命令 echo,顯示參數文本。(自動將參數排列成一行,超過一行的文本顯示爲單段落)
echo this is a test
傳遞到 echo 命令的任一個參數都會在(屏幕上)顯示出來。
路徑名展開
通配符所依賴的工作機制叫做路徑名展開。eg:
echo D*
echo *s
echo [[:upper:]]*
echo /usr/*/share
隱藏文件路徑名展開
echo * 不會顯示隱藏文件。
echo .* 可以顯示隱藏文件,但是結果中也會包括 "."(當前目錄) 和 ".."(父目錄) 這兩個名字。(注意:多個圓點開頭的文件名也屬於隱藏文件。)
波浪線展開
波浪線字符(“~”)用在 一個單詞的開頭時,它會展開成指定用戶的家目錄名,如果沒有指定用戶名,則展開成當前用戶的家目錄:
echo ~ username
算術表達式展開
shell 會在展開過程中執行算術表達式。所以我們可以把 shell 提示當作計算器來使用:
ldz@MSI:~$ echo $((2 + 2))
4
算術表達式展開使用這種格式:(美元符號加兩層圓括號)
$((expression))
注意:算術表達式只支持整數
操作符 | 說明 |
---|---|
+ | 加 |
- | 減 |
* | 乘 |
/ | 除(但是記住,因爲展開只是支持整數除法,所以結果是整數。) |
% | 取餘,只是簡單的意味着,“餘數” |
** | 取冪 |
算術表達式中空格並不重要,並且表達式可以嵌套。eg:
echo $(((5**2) * 3)) #也可以把每一層分別展開:echo $(($((5**2)) * 3))
算術表達式也可以嵌入文本中:
ldz@MSI:~$ echo Five divided by two equals $((5/2))
Five divided by two equals 2
花括號展開
通過花括號展開可以從一個包含花括號的模式中 創建多個文本字符串。eg:
ldz@MSI:~$ echo Front-{A,B,C}-Back
Front-A-Back Front-B-Back Front-C-Back
花括號展開模式可能包含一個開頭部分叫做報頭(如上例的Front-),一個結尾部分叫做附言(如上例的-Back)。花括號表達式本身可能包含一個由逗號分開的字符串列表,或者一個整數區間,或者單個的字符的區間。這種模式不能嵌入空白字符。
整數區間:
echo Number_{1..5}
倒序字母區間:
echo {Z..A}
花括號展開可以嵌套
ldz@MSI:~$ echo a{A{1,2},B{3,4}}b
aA1b aA2b aB3b aB4b
最常見的應用是,創建一系列的文件或目錄列表。
mkdir {2007..2009}-0{1..9} {2007..2009}-{10..12}
參數展開
這個特性在 shell 腳本中比直接在命令行中更有用。 它的許多功能和系統存儲小塊數據(變量),並給每塊數據命名的能力有關係。簡言之,就是變量值展開。格式爲美元符號加變量名($variable)。
eg:”USER”變量中存放的是用戶名
ldz@MSI:~$ echo $USER
ldz
如果是對未定義的變量進行參數展開會將其替換爲一個空字符串。eg:數字1是沒有定義的變量
ldz@MSI:~$ echo The total is $100.00
The total is 00.00
查看有效的變量列表
printenv | less
在其它展開類型中,如果你誤輸入一個模式,展開就不會發生。但在參數展開中,如果你拼寫錯了一個變量名, 展開仍然會進行,只是展開的結果是一個空字符串。
命令替換
命令替換即把一個命令的輸出作爲一個展開模式來使用:
echo $(ls)
ls -l $(which cp)
這裏我們把 which cp 的執行結果作爲一個參數傳遞給 ls 命令,因此可以在不知道 cp 命令 完整路徑名的情況下得到它的文件屬性列表。
不只限制於簡單命令,也可以使用整個管道線的輸出作爲展開模式。使管道線的輸出結果成爲 file 命令的參數列表。
file $(ls /usr/bin/* | grep zip)
bash中還支持舊版shell中的另一種語法,使用倒引號來替代美元符號和括號。
ls -l `which cp`
引用
shell 提供了一種 叫做引用的機制,來有選擇地禁止不需要的展開。
雙引號
放在雙引號中的文本都被當作普通字符來看待。但有幾個例外:$、\ 和 ` `(倒引號)。也就是說,單詞分割、路徑名展開、 波浪線展開和花括號展開都將失效,但參數展開、算術展開和命令替換仍然執行。
所謂單詞分割:Linux的單詞分割機制先查找是否存在空格、製表符以及換行符,然後將它們作爲單詞間的界定符號,並全部替換成爲單個空格。也就是說空格、製表符以及換行符都不會作爲文本的一部分,而是作爲分隔符使用,直接替換爲單個空格(刪除多餘的空格)。Linux通過識別這些來將單詞分割爲不同的參數。eg:
ldz@MSI:~$ echo this is a test
this is a test
如果我們想要阻止單詞分割,比如我們的文件名包含一個空格(即空格作爲名稱文本一部分),可以使用雙引號來保留空格文本:
ls -l "two words.txt"
如果不使用雙引號,系統會將文件名識別爲兩個參數:two 和 words.txt 。
注意,雙引號中參數展開、算術表達式展開和命令替換仍然有效:
ldz@MSI:~$ echo "$USER $((2+2)) $(cal)"
ldz 4 September 2018
Su Mo Tu We Th Fr Sa
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
單詞分割機制把換行符看作界定符,對命令替換產生了一個微妙但有趣的影響。
ldz@MSI:~$ echo $(cal)
September 2018 Su Mo Tu We Th Fr Sa 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
ldz@MSI:~$ echo "$(cal)"
September 2018
Su Mo Tu We Th Fr Sa
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
在第一個實例中,沒有引用的命令替換由於單詞分割的原因導致命令行包含38個參數。在第二個例子中, 命令行只有一個參數,參數中包括嵌入的空格和換行符。
單引號
單引號可以禁止所有的展開。
ldz@MSI:~$ echo 'text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER'
text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER
轉義字符
轉義字符(即反斜槓 “\”)用來引用單個字符。通常在雙引號中使用轉義字符,來有選擇地阻止展開。eg:
ldz@MSI:~$ echo "The balance for user $USER is: \$5.00"
The balance for user ldz is: $5.00
也常常使用轉義字符來消除文件名中一個字符的特殊含義,如“$”, “!”, “&”, “ “(空格) 等字符。eg:創建一個名爲"a a.txt"的文件
touch a\ a.txt
注意在單引號中,反斜槓也將失去它的特殊含義,被看作普通字符。
反斜槓轉義字符序列
反斜槓除了作爲轉義字符外,也可以構成一種表示法,來代表某種特殊字符,這些特殊字符叫做控制碼。ASCII 編碼表中前32個字符被用來把命令轉輸到電報機之類的設備。如製表符、退格符、換行符、回車符等,還有大家不太熟悉的如空值、傳輸結束碼、和確認。
常見的反斜槓轉義字符序列:
轉義序列 | 含義 |
\a | 響鈴(”警告”-導致計算機嘟嘟響) |
\b | 退格符 |
\n | 新的一行。在類 Unix 系統中,產生換行。 |
\r | 回車符 |
\t | 製表符 |
echo 命令帶上 ‘-e’ 選項,能夠解釋轉義序列。可以把轉義序列放在 $' ' (美元符號加單引號) 裏面或者" "(雙引號)裏面。eg:
sleep 10; echo -e "Time's up\a" #十秒後計算機發出一聲警告聲
sleep 10; echo "Time's up" $'\a' #同上
echo -e "\a" #發出警告聲
echo -e $'\a' #同上