Bash Commands and Shell Scripts
為了考試準備一下吧
這門課對這個領域的知識教的太淺,考的卻很難,必須要額外自學一點東西
Variables in Shell Scripts
首先是 Shell Scripts 中的變量概念: Shell Scripts 中的 變量只有一種類型 string
Define variable in shell scripts
對于一個變量賦值語句 a=xxx,xxx 必須是字符串 string
-
xxx是單引號括起來的a='hello': 單引號中的內容不支持 variable substitution -
xxx是雙引號括起來的a="$b": 雙引號中的內容支持 variable substitution -
xxx是反引號括起來的反引號
` `與$()符號的作用一致
反引號中的內容是 bash command: 注意,所有 bash command 的 output 都是 string
Access variable in shell scripts
使用符號 $ 來訪問變量
$a 可以視為將變量 a 中的字符串展開,即 variable substitution
Text Processing Command: grep, sed, awk and tr
文本處理一直是 shell script 出題的大頭,這里學習一下幾個常用的文本處理命令
grep
grep 是 按行 對文本處理的一個重要命令
grep the line with specific string
用法一:grep 'foo' bar.txt
最樸素的 grep 用法,在 bar.txt 文件里查找所有包括 string foo 的行并打印這些行
grep the line with specific pattern
用法二:grep -E 'fooPATTERN' bar.txt
有時候我們需要查找符合某個 模式 (Pattern) 的字符串,模式用 正則表達式 (regex, regular expression) 進行表示
加上 flag -E 后,grep 能夠找到所有能夠匹配 pattern fooPATTERN 的行并輸出
grep : flags
這里介紹一些常用的修飾 grep 的 flags:
-
-v(--invert-match)加上 flag
-v后,grep 將會輸出所有不匹配的行: 例如這個命令grep -E -v '^$' bar.txt代表輸出 bar.txt 中所有非空行的行由于正則表達式
^$代表一個空行,所以 grep 將會匹配上 bar.txt 中所有的空行;然而在 flag-v的作用下,grep 將會輸出所有未匹配的行,即非空行 -
-o(--only-matching)加上 flag
-o后,grep 將只輸出與模式 fooPATTERN 匹配的部分,一次匹配占一行,例如命令grep -E -o '[a-zA-Z]' bar.txt代表輸出 bar.txt 中所有的字母,一個字母占一行(加上 flag
-o后,grep 可以視作 對整個文本處理而不是按行進行處理,因為它將輸出所有匹配的模式,與行無關) -
-c(--count)加上 flag
-c后,grep 將只輸出匹配的行數(因此這個 flag 可以與
-v復合:-vc輸出不匹配的行數) -
-n(--line-number)加上 flag
-n后,grep 還將輸出匹配行的行號 (加在匹配行之前,格式為 lineNumber:matchingLine)(同樣,
-n也能與-v復合,在輸出不匹配行的同時輸出不匹配行的行號)
sed
接下來是 sed 命令,sed 實際上是 linux 里一個帶有 interface 的文本編輯器
以命令行的形式對 sed 進行調用即是 sed 命令,sed 也是 按行 對文本進行操作的,其可以實現文本的行插入,刪除與替換
其基礎語法是 sed '[script]' bar.txt
這里,script 的語法其實和 diff 命令后顯示的行的增刪情況很相似
add a line (a/i)
在 bar.txt 的第二行后添加新一行文本 "Joker": sed '2aJoker' bar.txt
在 bar.txt 的第三行前添加新一行文本 "Skull": sed '3iSkull' bar.txt
delete lines (d)
刪除 bar.txt 的第二行: sed '2d' bar.txt
刪除 bar.txt 的第一到第三行: sed '1,3d' bar.txt
刪除 bar.txt 的第三到最后一行: (使用 $ 符號): sed '3,$d' bar.txt
show lines (p)
顯示 bar.txt 的第二到第五行: sed -n '2,5p' bar.txt (注意一定要加 flag -n)
change lines (c)
將 bar.txt 的第二到第五行修改為 "mona": sed '2,5cmona' bar.txt
(相當于先將第二至五行刪除,再在新的第二行前添加一行 "mona")
search data
接下來是 sed 命令的高階用法,script 語法變得更加復雜: 不同的成分之間將用 / 進行分隔
并且,script 是支持正則表達式的,可以用正則表達式而不是純字符串來匹配模式
找到 bar.txt 所有包含 "oo" 的行:sed -n '/oo/p' bar.txt (這與 grep "oo" bar.txt 的作用相同)
找到 bar.txt 所有不包含 "oo" 的行 (即刪除所有包含 "oo" 的行并輸出剩余的部分): sed '/oo/d' bar.txt (這與 grep -v "oo" bar.txt 的作用相同)
將 bar.txt 中所有包含 "oo" 的行中第一次出現的 "oo" 修改成 "kk": sed 's/oo/kk/' bar.txt
只替換每行中第一次出現的字串顯然不具有很大的應用價值,我們加上標識符 g 來將所有 "oo" 替換成 "kk": sed 's/oo/kk/g' bar.txt
awk
awk 實際上也是一種文本編輯語言,語法上和 C 很類似,因此比 shell script 還容易理解
正因為如此,我們能在 awk 命令中執行 awk 語言的腳本,這使其能完成很復雜的文本格式化功能
根據未老師的介紹,sed 命令傾向于將一行看作一個整體修改,而 awk 則傾向于將一行看作很多字段的集合 (類似于表格)
awk 的基礎語法是 awk '[awk script]' bar.txt
這里介紹 awk 最基礎的一個用法,即 awk script 呈現 condition {action} 的語法結構
-
輸出 bar.txt 中所有字段數等于 \(3\) 的行 (分隔符為空格或制表符):
awk 'NF==3' bar.txt(實際上是awk 'NF==3 {print $0}' bar.txt的簡寫) -
輸出 bar.txt 中所有字段數等于 \(3\) 的行 (分隔符為逗號
,):awk -F',' 'NF==3' bar.txt -
輸出 bar.txt 中所有字段數等于 \(3\) 的行中的第一與第三個字段 (分隔符為空格或制表符):
awk 'NF==3 {print $1, $3}'
NF 是 awk 的內建變量,記錄的是當前行的字段數,還有其他的內建變量,例如:FS (字段分隔符,默認為任何 blank 字符), length (當前行的長度), NR (當前行的行號)
另外,我們甚至能在 awk script 用 awk 語言編寫程序 (和 C 語法極其相似),來完成一系列復雜的文本格式化
Eg. 1

對于這一系列雜亂的字段,我們想將其整理成一行中每兩個字段間只保留一個空格作為分隔符
這一過程用 C 語言來實現可以說非常簡單,但是 shell script 中我一時半會真的想不出來
此時,我們直接使用 awk 命令作為 shell 與 C 語言的橋梁: awk 'NF!=0 {{for (i=1; i < NF; ++i) printf "%s ", $i} print $NF}' b.txt
是不是一下變得簡單多了:我們先用 NF!=0 條件去掉空行,再進行 {} 中的 action 階段: 這里就幾乎全是 C 語言的語法了
效果如下:

Eg. 2
這是期中 Quiz 的一個題目,當時沒有接觸過 sed, awk, tr 命令的我可以說是一籌莫展
在學會這些命令以后,這種題可以說是迎刃而解


(將被若干空格,制表符,換行符等 [:space:] 符號分隔的字段整理成一行一個的形式,并且進行去重)
一行 shell script 就可以解決: tr '[[:space:]]' '\n' < $1 | awk 'NF != 0 && !a[$0]++' (思路來自 zrz)
真的很妙,直接用 awk 語言定義了一個桶,對字段進行統計,這樣就完成了去重 (注意,缺省的 action 默認是 print $0)
如果想要做的更絕點,可以只用一個 awk 命令 (雖然這樣本質上就是用 C 來寫而不是 shell script 了),即 awk '{for (i = 1;i <= NF; ++i) {{if (a[$i] == 0) print $i} a[$i]++}}' $1
總而言之,awk script 中可以包含這些內容: BEGIN{} (在文本處理前執行的 action), {} 逐行處理文本時執行的 action,END{} 處理完所有文本后執行的 action,而不在 {} 中的則是條件 condition (可缺省)
另外,awk 也能像 sed 一樣通過字符串或正則表達式進行模式匹配,這里就不多展開了,有 grep 與 sed 完成模式匹配的相關操作已經足夠了
tr
tr 命令的應用就比較簡單粗暴了,其完成的是對指定文本/模式的替換,和 sed 's/.../.../g' file 的功能幾乎一致 (sed 更強大,其不僅能轉換字符,還能轉換模式)
語法如下 tr '[SET1]' '[SET2]' < bar.txt,SET1 與 SET2 可以是字符集,也可以是字符串,但并不是模式!因此它不支持正則表達式描述的模式匹配,只能夠匹配字符串或字符集
-
將 bar.txt 中所有的空白字符 (空格,制表符...) 全部轉換為換行符:
tr '[[:blank:]]' '\n' < bar.txt -
將 bar.txt 中所有的小寫字母轉換為大寫字母:
tr '[[:lower:]]' '[[:upper:]]' < bar.txt(注意,當 SET1 與 SET2 是一一對應的關系時,字符的轉換才一一對應) -
刪除 bar.txt 中的所有空字符 (空格,制表符,換行符...) 使其稱為一條連續的字符串:
tr -d '[[:space:]]' < bar.txt(使用 flag-d進行刪除) -
使 bar.txt 中所有連續的字母縮減成只有一個 (例如,"ooookay!" 縮減為 "okay!"):
tr -s '[[:alpha:]]' < bar.txt(使用 flag-s)

浙公網安備 33010602011771號