## 10.6 管線命令 (pipe)
就如同前面所說的, bash 命令執行的時候有輸出的數據會出現! 那么如果這群數據必需要經過幾道手續之后才能得到我們所想要的格式,應該如何來設置? 這就牽涉到管線命令的問題了 (pipe) ,管線命令使用的是“ | ”這個界定符號! 另外,管線命令與“連續下達命令”是不一樣的呦! 這點下面我們會再說明。下面我們先舉一個例子來說明一下簡單的管線命令。
假設我們想要知道 /etc/ 下面有多少文件,那么可以利用 ls /etc 來查閱,不過, 因為 /etc 下面的文件太多,導致一口氣就將屏幕塞滿了~不知道前面輸出的內容是啥?此時,我們可以通過 less 指令的協助,利用:
```
[dmtsai@study ~]$ ls -al /etc | less
```
如此一來,使用 ls 指令輸出后的內容,就能夠被 less 讀取,并且利用 less 的功能,我們就能夠前后翻動相關的信息了!很方便是吧?我們就來了解一下這個管線命令“ | ”的用途吧! 其實這個管線命令“ | ”僅能處理經由前面一個指令傳來的正確信息,也就是 standard output 的信息,對于 stdandard error 并沒有直接處理的能力。那么整體的管線命令可以使用下圖表示:
圖10.6.1、管線命令的處理示意圖
在每個管線后面接的第一個數據必定是“指令”喔!而且這個指令必須要能夠接受 standard input 的數據才行,這樣的指令才可以是為“管線命令”,例如 less, more, head, tail 等都是可以接受 standard input 的管線命令啦。至于例如 ls, cp, mv 等就不是管線命令了!因為 ls, cp, mv 并不會接受來自 stdin 的數據。 也就是說,管線命令主要有兩個比較需要注意的地方:
* 管線命令僅會處理 standard output,對于 standard error output 會予以忽略
* 管線命令必須要能夠接受來自前一個指令的數據成為 standard input 繼續處理才行。

**Tips** 想一想,如果你硬要讓 standard error 可以被管線命令所使用,那該如何處理?其實就是通過上一小節的數據流重導向即可! 讓 2>&1 加入指令中~就可以讓 2> 變成 1> 啰!了解了嗎? ^_^
多說無益,讓我們來玩一些管線命令吧!下面的咚咚對系統管理非常有幫助喔!
### 10.6.1 擷取命令: cut, grep
什么是擷取命令啊?說穿了,就是將一段數據經過分析后,取出我們所想要的。或者是經由分析關鍵字,取得我們所想要的那一行! 不過,要注意的是,一般來說,擷取訊息通常是針對“一行一行”來分析的, 并不是整篇訊息分析的喔~下面我們介紹兩個很常用的訊息擷取命令:
* cut
cut 不就是“切”嗎?沒錯啦!這個指令可以將一段訊息的某一段給他“切”出來~ 處理的訊息是以“行”為單位喔!下面我們就來談一談:
```
[dmtsai@study ~]$ cut -d'分隔字符' -f fields <==用于有特定分隔字符
[dmtsai@study ~]$ cut -c 字符區間 <==用于排列整齊的訊息
選項與參數:
-d :后面接分隔字符。與 -f 一起使用;
-f :依據 -d 的分隔字符將一段訊息分區成為數段,用 -f 取出第幾段的意思;
-c :以字符 (characters) 的單位取出固定字符區間;
范例一:將 PATH 變量取出,我要找出第五個路徑。
[dmtsai@study ~]$ echo ${PATH}
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin
# 1 | 2 | 3 | 4 | 5 | 6 |
[dmtsai@study ~]$ echo ${PATH} | cut -d ':' -f 5
# 如同上面的數字顯示,我們是以“ : ”作為分隔,因此會出現 /home/dmtsai/.local/bin
# 那么如果想要列出第 3 與第 5 呢?,就是這樣:
[dmtsai@study ~]$ echo ${PATH} | cut -d ':' -f 3,5
范例二:將 export 輸出的訊息,取得第 12 字符以后的所有字串
[dmtsai@study ~]$ export
declare -x HISTCONTROL="ignoredups"
declare -x HISTSIZE="1000"
declare -x HOME="/home/dmtsai"
declare -x HOSTNAME="study.centos.vbird"
.....(其他省略).....
# 注意看,每個數據都是排列整齊的輸出!如果我們不想要“ declare -x ”時,就得這么做:
[dmtsai@study ~]$ export | cut -c 12-
HISTCONTROL="ignoredups"
HISTSIZE="1000"
HOME="/home/dmtsai"
HOSTNAME="study.centos.vbird"
.....(其他省略).....
# 知道怎么回事了吧?用 -c 可以處理比較具有格式的輸出數據!
# 我們還可以指定某個范圍的值,例如第 12-20 的字符,就是 cut -c 12-20 等等!
范例三:用 last 將顯示的登陸者的信息中,僅留下使用者大名
[dmtsai@study ~]$ last
root pts/1 192.168.201.101 Sat Feb 7 12:35 still logged in
root pts/1 192.168.201.101 Fri Feb 6 12:13 - 18:46 (06:33)
root pts/1 192.168.201.254 Thu Feb 5 22:37 - 23:53 (01:16)
# last 可以輸出“帳號/終端機/來源/日期時間”的數據,并且是排列整齊的
[dmtsai@study ~]$ last | cut -d ' ' -f 1
# 由輸出的結果我們可以發現第一個空白分隔的字段代表帳號,所以使用如上指令:
# 但是因為 root pts/1 之間空格有好幾個,并非僅有一個,所以,如果要找出
# pts/1 其實不能以 cut -d ' ' -f 1,2 喔!輸出的結果會不是我們想要的。
```
cut 主要的用途在于將“同一行里面的數據進行分解!”最常使用在分析一些數據或文字數據的時候! 這是因為有時候我們會以某些字符當作分區的參數,然后來將數據加以切割,以取得我們所需要的數據。 鳥哥也很常使用這個功能呢!尤其是在分析 log 文件的時候!不過,cut 在處理多空格相連的數據時,可能會比較吃力一點,所以某些時刻可能會使用下一章的 awk 來取代的!
* grep
剛剛的 cut 是將一行訊息當中,取出某部分我們想要的,而 grep 則是分析一行訊息, 若當中有我們所需要的信息,就將該行拿出來~簡單的語法是這樣的:
```
[dmtsai@study ~]$ grep [-acinv] [--color=auto] '搜尋字串' filename
選項與參數:
-a :將 binary 文件以 text 文件的方式搜尋數據
-c :計算找到 '搜尋字串' 的次數
-i :忽略大小寫的不同,所以大小寫視為相同
-n :順便輸出行號
-v :反向選擇,亦即顯示出沒有 '搜尋字串' 內容的那一行!
--color=auto :可以將找到的關鍵字部分加上顏色的顯示喔!
范例一:將 last 當中,有出現 root 的那一行就取出來;
[dmtsai@study ~]$ last | grep 'root'
范例二:與范例一相反,只要沒有 root 的就取出!
[dmtsai@study ~]$ last | grep -v 'root'
范例三:在 last 的輸出訊息中,只要有 root 就取出,并且僅取第一欄
[dmtsai@study ~]$ last | grep 'root' |cut -d ' ' -f1
# 在取出 root 之后,利用上個指令 cut 的處理,就能夠僅取得第一欄啰!
范例四:取出 /etc/man_db.conf 內含 MANPATH 的那幾行
[dmtsai@study ~]$ grep --color=auto 'MANPATH' /etc/man_db.conf
....(前面省略)....
MANPATH_MAP /usr/games /usr/share/man
MANPATH_MAP /opt/bin /opt/man
MANPATH_MAP /opt/sbin /opt/man
# 神奇的是,如果加上 --color=auto 的選項,找到的關鍵字部分會用特殊顏色顯示喔!
```
grep 是個很棒的指令喔!他支持的語法實在是太多了~用在正則表達式里頭, 能夠處理的數據實在是多的很~不過,我們這里先不談正則表達式~下一章再來說明~ 您先了解一下, grep 可以解析一行文字,取得關鍵字,若該行有存在關鍵字,就會整行列出來!另外, CentOS 7 當中,默認的 grep 已經主動加上 --color=auto 在 alias 內了喔!
### 10.6.2 排序命令: sort, wc, uniq
很多時候,我們都會去計算一次數據里頭的相同型態的數據總數,舉例來說, 使用 last 可以查得系統上面有登陸主機者的身份。那么我可以針對每個使用者查出他們的總登陸次數嗎? 此時就得要排序與計算之類的指令來輔助了!下面我們介紹幾個好用的排序與統計指令喔!
* sort
sort 是很有趣的指令,他可以幫我們進行排序,而且可以依據不同的數據型態來排序喔! 例如數字與文字的排序就不一樣。此外,排序的字符與語系的編碼有關,因此, 如果您需要排序時,建議使用 LANG=C 來讓語系統一,數據排序比較好一些。
```
[dmtsai@study ~]$ sort [-fbMnrtuk] [file or stdin]
選項與參數:
-f :忽略大小寫的差異,例如 A 與 a 視為編碼相同;
-b :忽略最前面的空白字符部分;
-M :以月份的名字來排序,例如 JAN, DEC 等等的排序方法;
-n :使用“純數字”進行排序(默認是以文字體態來排序的);
-r :反向排序;
-u :就是 uniq ,相同的數據中,僅出現一行代表;
-t :分隔符號,默認是用 [tab] 鍵來分隔;
-k :以那個區間 (field) 來進行排序的意思
范例一:個人帳號都記錄在 /etc/passwd 下,請將帳號進行排序。
[dmtsai@study ~]$ cat /etc/passwd | sort
abrt:x:173:173::/etc/abrt:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
alex:x:1001:1002::/home/alex:/bin/bash
# 鳥哥省略很多的輸出~由上面的數據看起來, sort 是默認“以第一個”數據來排序,
# 而且默認是以“文字”型態來排序的喔!所以由 a 開始排到最后啰!
范例二:/etc/passwd 內容是以 : 來分隔的,我想以第三欄來排序,該如何?
[dmtsai@study ~]$ cat /etc/passwd | sort -t ':' -k 3
root:x:0:0:root:/root:/bin/bash
dmtsai:x:1000:1000:dmtsai:/home/dmtsai:/bin/bash
alex:x:1001:1002::/home/alex:/bin/bash
arod:x:1002:1003::/home/arod:/bin/bash
# 看到特殊字體的輸出部分了吧?怎么會這樣排列啊?呵呵!沒錯啦~
# 如果是以文字體態來排序的話,原本就會是這樣,想要使用數字排序:
# cat /etc/passwd | sort -t ':' -k 3 -n
# 這樣才行啊!用那個 -n 來告知 sort 以數字來排序啊!
范例三:利用 last ,將輸出的數據僅取帳號,并加以排序
[dmtsai@study ~]$ last | cut -d ' ' -f1 | sort
```
sort 同樣是很常用的指令呢!因為我們常常需要比較一些信息啦! 舉個上面的第二個例子來說好了!今天假設你有很多的帳號,而且你想要知道最大的使用者 ID 目前到哪一號了!呵呵!使用 sort 一下子就可以知道答案咯!當然其使用還不止此啦!有空的話不妨玩一玩!
* uniq
如果我排序完成了,想要將重復的數據僅列出一個顯示,可以怎么做呢?
```
[dmtsai@study ~]$ uniq [-ic]
選項與參數:
-i :忽略大小寫字符的不同;
-c :進行計數
范例一:使用 last 將帳號列出,僅取出帳號欄,進行排序后僅取出一位;
[dmtsai@study ~]$ last | cut -d ' ' -f1 | sort | uniq
范例二:承上題,如果我還想要知道每個人的登陸總次數呢?
[dmtsai@study ~]$ last | cut -d ' ' -f1 | sort | uniq -c
1
6 (unknown
47 dmtsai
4 reboot
7 root
1 wtmp
# 從上面的結果可以發現 reboot 有 4 次, root 登陸則有 7 次!大部分是以 dmtsai 來操作!
# wtmp 與第一行的空白都是 last 的默認字符,那兩個可以忽略的!
```
這個指令用來將“重復的行刪除掉只顯示一個”,舉個例子來說, 你要知道這個月份登陸你主機的使用者有誰,而不在乎他的登陸次數,那么就使用上面的范例, (1)先將所有的數據列出;(2)再將人名獨立出來;(3)經過排序;(4)只顯示一個! 由于這個指令是在將重復的東西減少,所以當然需要“配合排序過的文件”來處理啰!
* wc
如果我想要知道 /etc/man_db.conf 這個文件里面有多少字?多少行?多少字符的話, 可以怎么做呢?其實可以利用 wc 這個指令來達成喔!他可以幫我們計算輸出的訊息的整體數據!
```
[dmtsai@study ~]$ wc [-lwm]
選項與參數:
-l :僅列出行;
-w :僅列出多少字(英文單字);
-m :多少字符;
范例一:那個 /etc/man_db.conf 里面到底有多少相關字、行、字符數?
[dmtsai@study ~]$ cat /etc/man_db.conf | wc
131 723 5171
# 輸出的三個數字中,分別代表: “行、字數、字符數”
范例二:我知道使用 last 可以輸出登陸者,但是 last 最后兩行并非帳號內容,那么請問,
我該如何以一行指令串取得登陸系統的總人次?
[dmtsai@study ~]$ last | grep [a-zA-Z] | grep -v 'wtmp' | grep -v 'reboot' | \
> grep -v 'unknown' |wc -l
# 由于 last 會輸出空白行, wtmp, unknown, reboot 等無關帳號登陸的信息,因此,我利用
# grep 取出非空白行,以及去除上述關鍵字那幾行,再計算行數,就能夠了解啰!
```
wc 也可以當作指令?這可不是上洗手間的 WC 呢!這是相當有用的計算文件內容的一個工具組喔!舉個例子來說, 當你要知道目前你的帳號文件中有多少個帳號時,就使用這個方法:“ cat /etc/passwd | wc -l ”啦!因為 /etc/passwd 里頭一行代表一個使用者呀! 所以知道行數就曉得有多少的帳號在里頭了!而如果要計算一個文件里頭有多少個字符時,就使用 wc -m 這個選項吧!
### 10.6.3 雙向重導向: tee
想個簡單的東西,我們由前一節知道 > 會將數據流整個傳送給文件或設備,因此我們除非去讀取該文件或設備, 否則就無法繼續利用這個數據流。萬一我想要將這個數據流的處理過程中將某段訊息存下來,應該怎么做? 利用 tee 就可以啰~我們可以這樣簡單的看一下:
圖10.6.2、tee 的工作流程示意圖
tee 會同時將數據流分送到文件去與屏幕 (screen);而輸出到屏幕的,其實就是 stdout ,那就可以讓下個指令繼續處理喔!
```
[dmtsai@study ~]$ tee [-a] file
選項與參數:
-a :以累加 (append) 的方式,將數據加入 file 當中!
[dmtsai@study ~]$ last | tee last.list | cut -d " " -f1
# 這個范例可以讓我們將 last 的輸出存一份到 last.list 文件中;
[dmtsai@study ~]$ ls -l /home | tee ~/homefile | more
# 這個范例則是將 ls 的數據存一份到 ~/homefile ,同時屏幕也有輸出訊息!
[dmtsai@study ~]$ ls -l / | tee -a ~/homefile | more
# 要注意! tee 后接的文件會被覆蓋,若加上 -a 這個選項則能將訊息累加。
```
tee 可以讓 standard output 轉存一份到文件內并將同樣的數據繼續送到屏幕去處理! 這樣除了可以讓我們同時分析一份數據并記錄下來之外,還可以作為處理一份數據的中間暫存盤記錄之用! tee 這家伙在很多選擇/填充的認證考試中很容易考呢!
### 10.6.4 字符轉換命令: tr, col, join, paste, expand
我們在 [vim 程序編輯器](../Text/index.html#tips_dos)當中,提到過 DOS 斷行字符與 Unix 斷行字符的不同,并且可以使用 dos2unix 與 unix2dos 來完成轉換。好了,那么思考一下,是否還有其他常用的字符替代? 舉例來說,要將大寫改成小寫,或者是將數據中的 [tab] 按鍵轉成空白鍵?還有,如何將兩篇訊息整合成一篇? 下面我們就來介紹一下這些字符轉換命令在管線當中的使用方法:
* tr
tr 可以用來刪除一段訊息當中的文字,或者是進行文字訊息的替換!
```
[dmtsai@study ~]$ tr [-ds] SET1 ...
選項與參數:
-d :刪除訊息當中的 SET1 這個字串;
-s :取代掉重復的字符!
范例一:將 last 輸出的訊息中,所有的小寫變成大寫字符:
[dmtsai@study ~]$ last | tr '[a-z]' '[A-Z]'
# 事實上,沒有加上單引號也是可以執行的,如:“ last | tr [a-z] [A-Z] ”
范例二:將 /etc/passwd 輸出的訊息中,將冒號 (:) 刪除
[dmtsai@study ~]$ cat /etc/passwd | tr -d ':'
范例三:將 /etc/passwd 轉存成 dos 斷行到 /root/passwd 中,再將 ^M 符號刪除
[dmtsai@study ~]$ cp /etc/passwd ~/passwd && unix2dos ~/passwd
[dmtsai@study ~]$ file /etc/passwd ~/passwd
/etc/passwd: ASCII text
/home/dmtsai/passwd: ASCII text, with CRLF line terminators <==就是 DOS 斷行
[dmtsai@study ~]$ cat ~/passwd | tr -d '\r' > ~/passwd.linux
# 那個 \r 指的是 DOS 的斷行字符,關于更多的字符,請參考 man tr
[dmtsai@study ~]$ ll /etc/passwd ~/passwd*
-rw-r--r--. 1 root root 2092 Jun 17 00:20 /etc/passwd
-rw-r--r--. 1 dmtsai dmtsai 2133 Jul 9 22:13 /home/dmtsai/passwd
-rw-rw-r--. 1 dmtsai dmtsai 2092 Jul 9 22:13 /home/dmtsai/passwd.linux
# 處理過后,發現文件大小與原本的 /etc/passwd 就一致了!
```
其實這個指令也可以寫在“正則表達式”里頭!因為他也是由正則表達式的方式來取代數據的! 以上面的例子來說,使用 [] 可以設置一串字呢!也常常用來取代文件中的怪異符號! 例如上面第三個例子當中,可以去除 DOS 文件留下來的 ^M 這個斷行的符號!這東西相當的有用!相信處理 Linux & Windows 系統中的人們最麻煩的一件事就是這個事情啦!亦即是 DOS 下面會自動的在每行行尾加入 ^M 這個斷行符號!這個時候除了以前講過的 dos2unix 之外,我們也可以使用這個 tr 來將 ^M 去除! ^M 可以使用 \r 來代替之!
* col
```
[dmtsai@study ~]$ col [-xb]
選項與參數:
-x :將 tab 鍵轉換成對等的空白鍵
范例一:利用 cat -A 顯示出所有特殊按鍵,最后以 col 將 [tab] 轉成空白
[dmtsai@study ~]$ cat -A /etc/man_db.conf <==此時會看到很多 ^I 的符號,那就是 tab
[dmtsai@study ~]$ cat /etc/man_db.conf | col -x | cat -A | more
# 嘿嘿!如此一來, [tab] 按鍵會被取代成為空白鍵,輸出就美觀多了!
```
雖然 col 有他特殊的用途,不過,很多時候,他可以用來簡單的處理將 [tab] 按鍵取代成為空白鍵! 例如上面的例子當中,如果使用 cat -A 則 [tab] 會以 ^I 來表示。 但經過 col -x 的處理,則會將 [tab] 取代成為對等的空白鍵!
* join
join 看字面上的意義 (加入/參加) 就可以知道,他是在處理兩個文件之間的數據, 而且,主要是在處理“兩個文件當中,有 **"相同數據"** 的那一行,才將他加在一起”的意思。我們利用下面的簡單例子來說明:
```
[dmtsai@study ~]$ join [-ti12] file1 file2
選項與參數:
-t :join 默認以空白字符分隔數據,并且比對“第一個字段”的數據,
如果兩個文件相同,則將兩筆數據聯成一行,且第一個字段放在第一個!
-i :忽略大小寫的差異;
-1 :這個是數字的 1 ,代表“第一個文件要用那個字段來分析”的意思;
-2 :代表“第二個文件要用那個字段來分析”的意思。
范例一:用 root 的身份,將 /etc/passwd 與 /etc/shadow 相關數據整合成一欄
[root@study ~]# head -n 3 /etc/passwd /etc/shadow
==> /etc/passwd <==
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
==> /etc/shadow <==
root:$6$wtbCCce/PxMeE5wm$KE2IfSJr...:16559:0:99999:7:::
bin:*:16372:0:99999:7:::
daemon:*:16372:0:99999:7:::
# 由輸出的數據可以發現這兩個文件的最左邊字段都是相同帳號!且以 : 分隔
[root@study ~]# join -t ':' /etc/passwd /etc/shadow | head -n 3
root:x:0:0:root:/root:/bin/bash:$6$wtbCCce/PxMeE5wm$KE2IfSJr...:16559:0:99999:7:::
bin:x:1:1:bin:/bin:/sbin/nologin:*:16372:0:99999:7:::
daemon:x:2:2:daemon:/sbin:/sbin/nologin:*:16372:0:99999:7:::
# 通過上面這個動作,我們可以將兩個文件第一字段相同者整合成一列!
# 第二個文件的相同字段并不會顯示(因為已經在最左邊的字段出現了啊!)
范例二:我們知道 /etc/passwd 第四個字段是 GID ,那個 GID 記錄在
/etc/group 當中的第三個字段,請問如何將兩個文件整合?
[root@study ~]# head -n 3 /etc/passwd /etc/group
==> /etc/passwd <==
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
==> /etc/group <==
root:x:0:
bin:x:1:
daemon:x:2:
# 從上面可以看到,確實有相同的部分喔!趕緊來整合一下!
[root@study ~]# join -t ':' -1 4 /etc/passwd -2 3 /etc/group | head -n 3
0:root:x:0:root:/root:/bin/bash:root:x:
1:bin:x:1:bin:/bin:/sbin/nologin:bin:x:
2:daemon:x:2:daemon:/sbin:/sbin/nologin:daemon:x:
# 同樣的,相同的字段部分被移動到最前面了!所以第二個文件的內容就沒再顯示。
# 請讀者們配合上述顯示兩個文件的實際內容來比對!
```
這個 join 在處理兩個相關的數據文件時,就真的是很有幫助的啦! 例如上面的案例當中,我的 /etc/passwd, /etc/shadow, /etc/group 都是有相關性的, 其中 /etc/passwd, /etc/shadow 以帳號為相關性,至于 /etc/passwd, /etc/group 則以所謂的 GID (帳號的數字定義) 來作為他的相關性。根據這個相關性, 我們可以將有關系的數據放置在一起!這在處理數據可是相當有幫助的! 但是上面的例子有點難,希望您可以靜下心好好的看一看原因喔!
此外,需要特別注意的是,在使用 join 之前,你所需要處理的文件應該要事先經過排序 (sort) 處理! 否則有些比對的項目會被略過呢!特別注意了!
* paste
這個 paste 就要比 join 簡單多了!相對于 join 必須要比對兩個文件的數據相關性, paste 就直接“將兩行貼在一起,且中間以 [tab] 鍵隔開”而已!簡單的使用方法:
```
[dmtsai@study ~]$ paste [-d] file1 file2
選項與參數:
-d :后面可以接分隔字符。默認是以 [tab] 來分隔的!
- :如果 file 部分寫成 - ,表示來自 standard input 的數據的意思。
范例一:用 root 身份,將 /etc/passwd 與 /etc/shadow 同一行貼在一起
[root@study ~]# paste /etc/passwd /etc/shadow
root:x:0:0:root:/root:/bin/bash root:$6$wtbCCce/PxMeE5wm$KE2IfSJr...:16559:0:99999:7:::
bin:x:1:1:bin:/bin:/sbin/nologin bin:*:16372:0:99999:7:::
daemon:x:2:2:daemon:/sbin:/sbin/nologin daemon:*:16372:0:99999:7:::
# 注意喔!同一行中間是以 [tab] 按鍵隔開的!
范例二:先將 /etc/group 讀出(用 cat),然后與范例一貼上一起!且僅取出前三行
[root@study ~]# cat /etc/group|paste /etc/passwd /etc/shadow -|head -n 3
# 這個例子的重點在那個 - 的使用!那玩意兒常常代表 stdin 喔!
```
* expand
這玩意兒就是在將 [tab] 按鍵轉成空白鍵啦~可以這樣玩:
```
[dmtsai@study ~]$ expand [-t] file
選項與參數:
-t :后面可以接數字。一般來說,一個 tab 按鍵可以用 8 個空白鍵取代。
我們也可以自行定義一個 [tab] 按鍵代表多少個字符呢!
范例一:將 /etc/man_db.conf 內行首為 MANPATH 的字樣就取出;僅取前三行;
[dmtsai@study ~]$ grep '^MANPATH' /etc/man_db.conf | head -n 3
MANPATH_MAP /bin /usr/share/man
MANPATH_MAP /usr/bin /usr/share/man
MANPATH_MAP /sbin /usr/share/man
# 行首的代表標志為 ^ ,這個我們留待下節介紹!先有概念即可!
范例二:承上,如果我想要將所有的符號都列出來?(用 cat)
[dmtsai@study ~]$ grep '^MANPATH' /etc/man_db.conf | head -n 3 |cat -A
MANPATH_MAP^I/bin^I^I^I/usr/share/man$
MANPATH_MAP^I/usr/bin^I^I/usr/share/man$
MANPATH_MAP^I/sbin^I^I^I/usr/share/man$
# 發現差別了嗎?沒錯~ [tab] 按鍵可以被 cat -A 顯示成為 ^I
范例三:承上,我將 [tab] 按鍵設置成 6 個字符的話?
[dmtsai@study ~]$ grep '^MANPATH' /etc/man_db.conf | head -n 3 | expand -t 6 - | cat -A
MANPATH_MAP /bin /usr/share/man$
MANPATH_MAP /usr/bin /usr/share/man$
MANPATH_MAP /sbin /usr/share/man$
123456123456123456123456123456123456123456123456...
# 仔細看一下上面的數字說明,因為我是以 6 個字符來代表一個 [tab] 的長度,所以,
# MAN... 到 /usr 之間會隔 12 (兩個 [tab]) 個字符喔!如果 tab 改成 9 的話,
# 情況就又不同了!這里也不好理解~您可以多設置幾個數字來查閱就曉得!
```
expand 也是挺好玩的~他會自動將 [tab] 轉成空白鍵~所以,以上面的例子來說, 使用 cat -A 就會查不到 ^I 的字符啰~此外,因為 [tab] 最大的功能就是格式排列整齊! 我們轉成空白鍵后,這個空白鍵也會依據我們自己的定義來增加大小~ 所以,并不是一個 ^I 就會換成 8 個空白喔!這個地方要特別注意的哩! 此外,您也可以參考一下 unexpand 這個將空白轉成 [tab] 的指令功能啊! ^_^
### 10.6.5 分區命令: split
如果你有文件太大,導致一些攜帶式設備無法復制的問題,嘿嘿!找 split 就對了! 他可以幫你將一個大文件,依據文件大小或行數來分區,就可以將大文件分區成為小文件了! 快速又有效啊!真不錯~
```
[dmtsai@study ~]$ split [-bl] file PREFIX
選項與參數:
-b :后面可接欲分區成的文件大小,可加單位,例如 b, k, m 等;
-l :以行數來進行分區。
PREFIX :代表前置字符的意思,可作為分區文件的前導文字。
范例一:我的 /etc/services 有六百多K,若想要分成 300K 一個文件時?
[dmtsai@study ~]$ cd /tmp; split -b 300k /etc/services services
[dmtsai@study tmp]$ ll -k services*
-rw-rw-r--. 1 dmtsai dmtsai 307200 Jul 9 22:52 servicesaa
-rw-rw-r--. 1 dmtsai dmtsai 307200 Jul 9 22:52 servicesab
-rw-rw-r--. 1 dmtsai dmtsai 55893 Jul 9 22:52 servicesac
# 那個文件名可以隨意取的啦!我們只要寫上前導文字,小文件就會以
# xxxaa, xxxab, xxxac 等方式來創建小文件的!
范例二:如何將上面的三個小文件合成一個文件,文件名為 servicesback
[dmtsai@study tmp]$ cat services* >> servicesback
# 很簡單吧?就用數據流重導向就好啦!簡單!
范例三:使用 ls -al / 輸出的信息中,每十行記錄成一個文件
[dmtsai@study tmp]$ ls -al / | split -l 10 - lsroot
[dmtsai@study tmp]$ wc -l lsroot*
10 lsrootaa
10 lsrootab
4 lsrootac
24 total
# 重點在那個 - 啦!一般來說,如果需要 stdout/stdin 時,但偏偏又沒有文件,
# 有的只是 - 時,那么那個 - 就會被當成 stdin 或 stdout ~
```
在 Windows 操作系統下,你要將文件分區需要如何作?傷腦筋吧!在 Linux 下面就簡單的多了!你要將文件分區的話,那么就使用 -b size 來將一個分區的文件限制其大小,如果是行數的話,那么就使用 -l line 來分區!好用的很!如此一來,你就可以輕易的將你的文件分區成某些軟件能夠支持的最大容量 (例如 gmail 單一信件 25MB 之類的!),方便你 copy 啰!
### 10.6.6 參數代換: xargs
xargs 是在做什么的呢?就以字面上的意義來看, x 是加減乘除的乘號,args 則是 arguments (參數) 的意思,所以說,這個玩意兒就是在產生某個指令的參數的意思! xargs 可以讀入 stdin 的數據,并且以空白字符或斷行字符作為分辨,將 stdin 的數據分隔成為 arguments 。 因為是以空白字符作為分隔,所以,如果有一些文件名或者是其他意義的名詞內含有空白字符的時候, xargs 可能就會誤判了~他的用法其實也還滿簡單的!就來看一看先!
```
[dmtsai@study ~]$ xargs [-0epn] command
選項與參數:
-0 :如果輸入的 stdin 含有特殊字符,例如 `, \, 空白鍵等等字符時,這個 -0 參數
可以將他還原成一般字符。這個參數可以用于特殊狀態喔!
-e :這個是 EOF (end of file) 的意思。后面可以接一個字串,當 xargs 分析到這個字串時,
就會停止繼續工作!
-p :在執行每個指令的 argument 時,都會詢問使用者的意思;
-n :后面接次數,每次 command 指令執行時,要使用幾個參數的意思。
當 xargs 后面沒有接任何的指令時,默認是以 echo 來進行輸出喔!
范例一:將 /etc/passwd 內的第一欄取出,僅取三行,使用 id 這個指令將每個帳號內容秀出來
[dmtsai@study ~]$ id root
uid=0(root) gid=0(root) groups=0(root) # 這個 id 指令可以查詢使用者的 UID/GID 等信息
[dmtsai@study ~]$ id $(cut -d ':' -f 1 /etc/passwd | head -n 3)
# 雖然使用 $(cmd) 可以預先取得參數,但可惜的是, id 這個指令“僅”能接受一個參數而已!
# 所以上述的這個指令執行會出現錯誤!根本不會顯示用戶的 ID 啊!
[dmtsai@study ~]$ cut -d ':' -f 1 /etc/passwd | head -n 3 | id
uid=1000(dmtsai) gid=1000(dmtsai) groups=1000(dmtsai),10(wheel) # 我不是要查自己啊!
# 因為 id 并不是管線命令,因此在上面這個指令執行后,前面的東西通通不見!只會執行 id!
[dmtsai@study ~]$ cut -d ':' -f 1 /etc/passwd | head -n 3 | xargs id
# 依舊會出現錯誤!這是因為 xargs 一口氣將全部的數據通通丟給 id 處理~但 id 就接受 1 個啊最多!
[dmtsai@study ~]$ cut -d ':' -f 1 /etc/passwd | head -n 3 | xargs -n 1 id
uid=0(root) gid=0(root) groups=0(root)
uid=1(bin) gid=1(bin) groups=1(bin)
uid=2(daemon) gid=2(daemon) groups=2(daemon)
# 通過 -n 來處理,一次給予一個參數,因此上述的結果就 OK 正常的顯示啰!
范例二:同上,但是每次執行 id 時,都要詢問使用者是否動作?
[dmtsai@study ~]$ cut -d ':' -f 1 /etc/passwd | head -n 3 | xargs -p -n 1 id
id root ?...y
uid=0(root) gid=0(root) groups=0(root)
id bin ?...y
.....(下面省略).....
# 呵呵!這個 -p 的選項可以讓使用者的使用過程中,被詢問到每個指令是否執行!
范例三:將所有的 /etc/passwd 內的帳號都以 id 查閱,但查到 sync 就結束指令串
[dmtsai@study ~]$ cut -d ':' -f 1 /etc/passwd | xargs -e'sync' -n 1 id
# 仔細與上面的案例做比較。也同時注意,那個 -e'sync' 是連在一起的,中間沒有空白鍵。
# 上個例子當中,第六個參數是 sync 啊,那么我們下達 -e'sync' 后,則分析到 sync 這個字串時,
# 后面的其他 stdin 的內容就會被 xargs 舍棄掉了!
```
其實,在 man xargs 里面就有三四個小范例,您可以自行參考一下內容。 此外, xargs 真的是很好用的一個玩意兒!您真的需要好好的參詳參詳!會使用 xargs 的原因是, 很多指令其實并不支持管線命令,因此我們可以通過 xargs 來提供該指令引用 standard input 之用!舉例來說,我們使用如下的范例來說明:
```
范例四:找出 /usr/sbin 下面具有特殊權限的文件名,并使用 ls -l 列出詳細屬性
[dmtsai@study ~]$ find /usr/sbin -perm /7000 | xargs ls -l
-rwx--s--x. 1 root lock 11208 Jun 10 2014 /usr/sbin/lockdev
-rwsr-xr-x. 1 root root 113400 Mar 6 12:17 /usr/sbin/mount.nfs
-rwxr-sr-x. 1 root root 11208 Mar 6 11:05 /usr/sbin/netreport
.....(下面省略).....
# 聰明的讀者應該會想到使用“ ls -l $(find /usr/sbin -perm /7000) ”來處理這個范例!
# 都 OK!能解決問題的方法,就是好方法!
```
### 10.6.7 關于減號 - 的用途
管線命令在 bash 的連續的處理程序中是相當重要的!另外,在 log file 的分析當中也是相當重要的一環, 所以請特別留意!另外,在管線命令當中,常常會使用到前一個指令的 stdout 作為這次的 stdin , 某些指令需要用到文件名稱 (例如 tar) 來進行處理時,該 stdin 與 stdout 可以利用減號 "-" 來替代, 舉例來說:
```
[root@study ~]# mkdir /tmp/homeback
[root@study ~]# tar -cvf - /home | tar -xvf - -C /tmp/homeback
```
上面這個例子是說:“我將 /home 里面的文件給他打包,但打包的數據不是紀錄到文件,而是傳送到 stdout; 經過管線后,將 tar -cvf - /home 傳送給后面的 tar -xvf - ”。后面的這個 - 則是取用前一個指令的 stdout, 因此,我們就不需要使用 filename 了!這是很常見的例子喔!注意注意!
- 鳥哥的Linux私房菜:基礎學習篇 第四版
- 目錄及概述
- 第零章、計算機概論
- 0.1 電腦:輔助人腦的好工具
- 0.2 個人電腦架構與相關設備元件
- 0.3 數據表示方式
- 0.4 軟件程序運行
- 0.5 重點回顧
- 0.6 本章習題
- 0.7 參考資料與延伸閱讀
- 第一章、Linux是什么與如何學習
- 1.1 Linux是什么
- 1.2 Torvalds的Linux發展
- 1.3 Linux當前應用的角色
- 1.4 Linux 該如何學習
- 1.5 重點回顧
- 1.6 本章習題
- 1.7 參考資料與延伸閱讀
- 第二章、主機規劃與磁盤分區
- 2.1 Linux與硬件的搭配
- 2.2 磁盤分區
- 2.3 安裝Linux前的規劃
- 2.4 重點回顧
- 2.5 本章習題
- 2.6 參考資料與延伸閱讀
- 第三章、安裝 CentOS7.x
- 3.1 本練習機的規劃--尤其是分區參數
- 3.2 開始安裝CentOS 7
- 3.3 多重開機安裝流程與管理(Option)
- 3.4 重點回顧
- 3.5 本章習題
- 3.6 參考資料與延伸閱讀
- 第四章、首次登陸與線上求助
- 4.1 首次登陸系統
- 4.2 文字模式下指令的下達
- 4.3 Linux系統的線上求助man page與info page
- 4.4 超簡單文書編輯器: nano
- 4.5 正確的關機方法
- 4.6 重點回顧
- 4.7 本章習題
- 4.8 參考資料與延伸閱讀
- 第五章、Linux 的文件權限與目錄配置
- 5.1 使用者與群組
- 5.2 Linux 文件權限概念
- 5.3 Linux目錄配置
- 5.4 重點回顧
- 5.5 本章練習
- 5.6 參考資料與延伸閱讀
- 第六章、Linux 文件與目錄管理
- 6.1 目錄與路徑
- 6.2 文件與目錄管理
- 6.3 文件內容查閱
- 6.4 文件與目錄的默認權限與隱藏權限
- 6.5 指令與文件的搜尋
- 6.6 極重要的復習!權限與指令間的關系
- 6.7 重點回顧
- 6.8 本章習題:
- 6.9 參考資料與延伸閱讀
- 第七章、Linux 磁盤與文件系統管理
- 7.1 認識 Linux 文件系統
- 7.2 文件系統的簡單操作
- 7.3 磁盤的分區、格式化、檢驗與掛載
- 7.4 設置開機掛載
- 7.5 內存交換空間(swap)之創建
- 7.6 文件系統的特殊觀察與操作
- 7.7 重點回顧
- 7.8 本章習題 - 第一題一定要做
- 7.9 參考資料與延伸閱讀
- 第八章、文件與文件系統的壓縮,打包與備份
- 8.1 壓縮文件的用途與技術
- 8.2 Linux 系統常見的壓縮指令
- 8.3 打包指令: tar
- 8.4 XFS 文件系統的備份與還原
- 8.5 光盤寫入工具
- 8.6 其他常見的壓縮與備份工具
- 8.7 重點回顧
- 8.8 本章習題
- 8.9 參考資料與延伸閱讀
- 第九章、vim 程序編輯器
- 9.1 vi 與 vim
- 9.2 vi 的使用
- 9.3 vim 的額外功能
- 9.4 其他 vim 使用注意事項
- 9.5 重點回顧
- 9.6 本章練習
- 9.7 參考資料與延伸閱讀
- 第十章、認識與學習BASH
- 10.1 認識 BASH 這個 Shell
- 10.2 Shell 的變量功能
- 10.3 命令別名與歷史命令
- 10.4 Bash Shell 的操作環境:
- 10.5 數據流重導向
- 10.6 管線命令 (pipe)
- 10.7 重點回顧
- 10.8 本章習題
- 10.9 參考資料與延伸閱讀
- 第十一章、正則表達式與文件格式化處理
- 11.1 開始之前:什么是正則表達式
- 11.2 基礎正則表達式
- 11.3 延伸正則表達式
- 11.4 文件的格式化與相關處理
- 11.5 重點回顧
- 11.6 本章習題
- 11.7 參考資料與延伸閱讀
- 第十二章、學習 Shell Scripts
- 12.1 什么是 Shell scripts
- 12.2 簡單的 shell script 練習
- 12.3 善用判斷式
- 12.4 條件判斷式
- 12.5 循環 (loop)
- 12.6 shell script 的追蹤與 debug
- 12.7 重點回顧
- 12.8 本章習題
- 第十三章、Linux 帳號管理與 ACL 權限設置
- 13.1 Linux 的帳號與群組
- 13.2 帳號管理
- 13.3 主機的細部權限規劃:ACL 的使用
- 13.4 使用者身份切換
- 13.5 使用者的特殊 shell 與 PAM 模塊
- 13.6 Linux 主機上的使用者訊息傳遞
- 13.7 CentOS 7 環境下大量創建帳號的方法
- 13.8 重點回顧
- 13.9 本章習題
- 13.10 參考資料與延伸閱讀
- 第十四章、磁盤配額(Quota)與進階文件系統管理
- 14.1 磁盤配額 (Quota) 的應用與實作
- 14.2 軟件磁盤陣列 (Software RAID)
- 14.3 邏輯卷軸管理員 (Logical Volume Manager)
- 14.4 重點回顧
- 14.5 本章習題
- 14.6 參考資料與延伸閱讀
- 第十五章、例行性工作調度(crontab)
- 15.1 什么是例行性工作調度
- 15.2 僅執行一次的工作調度
- 15.3 循環執行的例行性工作調度
- 15.4 可喚醒停機期間的工作任務
- 15.5 重點回顧
- 15.6 本章習題
- 第十六章、程序管理與 SELinux 初探
- 16.1 什么是程序 (process)
- 16.2 工作管理 (job control)
- 16.3 程序管理
- 16.4 特殊文件與程序
- 16.5 SELinux 初探
- 16.6 重點回顧
- 16.7 本章習題
- 16.8 參考資料與延伸閱讀
- 第十七章、認識系統服務 (daemons)
- 17.1 什么是 daemon 與服務 (service)
- 17.2 通過 systemctl 管理服務
- 17.3 systemctl 針對 service 類型的配置文件
- 17.4 systemctl 針對 timer 的配置文件
- 17.5 CentOS 7.x 默認啟動的服務簡易說明
- 17.6 重點回顧
- 17.7 本章習題
- 17.8 參考資料與延伸閱讀
- 第十八章、認識與分析登錄文件
- 18.1 什么是登錄文件
- 18.2 rsyslog.service :記錄登錄文件的服務
- 18.3 登錄文件的輪替(logrotate)
- 18.4 systemd-journald.service 簡介
- 18.5 分析登錄文件
- 18.6 重點回顧
- 18.7 本章習題
- 18.8 參考資料與延伸閱讀
- 第十九章、開機流程、模塊管理與 Loader
- 19.1 Linux 的開機流程分析
- 19.2 核心與核心模塊
- 19.3 Boot Loader: Grub2
- 19.4 開機過程的問題解決
- 19.5 重點回顧
- 19.6 本章習題
- 19.7 參考資料與延伸閱讀
- 第二十章、基礎系統設置與備份策略
- 20.1 系統基本設置
- 20.2 服務器硬件數據的收集
- 20.3 備份要點
- 20.4 備份的種類、頻率與工具的選擇
- 20.5 鳥哥的備份策略
- 20.6 災難復原的考慮
- 20.7 重點回顧
- 20.8 本章習題
- 20.9 參考資料與延伸閱讀
- 第二十一章、軟件安裝:源代碼與 Tarball
- 20.1 開放源碼的軟件安裝與升級簡介
- 21.2 使用傳統程序語言進行編譯的簡單范例
- 21.3 用 make 進行宏編譯
- 21.4 Tarball 的管理與建議
- 21.5 函數庫管理
- 21.6 檢驗軟件正確性
- 21.7 重點回顧
- 21.8 本章習題
- 21.9 參考資料與延伸閱讀
- 第二十二章、軟件安裝 RPM, SRPM 與 YUM
- 22.1 軟件管理員簡介
- 22.2 RPM 軟件管理程序: rpm
- 22.3 YUM 線上升級機制
- 22.4 SRPM 的使用 : rpmbuild (Optional)
- 22.5 重點回顧
- 22.6 本章習題
- 22.7 參考資料與延伸閱讀
- 第二十三章、X Window 設置介紹
- 23.1 什么是 X Window System
- 23.2 X Server 配置文件解析與設置
- 23.3 顯卡驅動程序安裝范例
- 23.4 重點回顧
- 23.5 本章習題
- 23.6 參考資料與延伸閱讀
- 第二十四章、Linux 核心編譯與管理
- 24.1 編譯前的任務:認識核心與取得核心源代碼
- 24.2 核心編譯的前處理與核心功能選擇
- 24.3 核心的編譯與安裝
- 24.4 額外(單一)核心模塊編譯
- 24.5 以最新核心版本編譯 CentOS 7.x 的核心
- 24.6 重點回顧
- 24.7 本章習題
- 24.8 參考資料與延伸閱讀