學(xué)習(xí)資料:《linux大棚命令百篇上》
什么是sed
sed是stream editor的縮寫,翻譯過來就是“流編輯器”思杯,而實(shí)際上它的作用也正如描述的那樣。sed命令是一個(gè)面向行處理的工具挠进, 以行為處理單位色乾,可以實(shí)現(xiàn)很強(qiáng)大的處理能力。sed對(duì)每一行進(jìn)行處理后輸出到標(biāo)準(zhǔn)輸出领突,反應(yīng)在我們可見的角度就是暖璧,sed會(huì)將修改的內(nèi)容輸出到shell上顯示出來,而不會(huì)對(duì)源文件做任何修改(當(dāng)然如果想對(duì)源文件進(jìn)行修改也是可以的)君旦。
像學(xué)習(xí)數(shù)學(xué)一樣澎办,sed的用法可以用一個(gè)公式概括:
sed [選項(xiàng)] command file
其中:
- command:對(duì)文件每行(或者選定行)進(jìn)行的操作。
- file:要操作的文件金砍。
sed的工作原理
sed在按行處理文件時(shí)局蚀,會(huì)將要處理的那行放入緩沖區(qū)中,然后sed命令會(huì)對(duì)緩沖區(qū)中的內(nèi)容進(jìn)行處理恕稠。處理完成后將緩沖區(qū)的內(nèi)容送入屏幕顯示琅绅,接著處理下一行,直到文件結(jié)束谱俭。所以整個(gè)處理過程中奉件,sed操作的都是緩沖區(qū)的內(nèi)容,因而并不會(huì)對(duì)源文件進(jìn)行任何修改昆著。
下面看一個(gè)小例子來展示一下sed的魅力:
首先用一個(gè)腳本來創(chuàng)建一個(gè)包含行號(hào)文件
#! /bin/bash
for i in {1..5}
do
echo "hello linux $i" >> hello
done
運(yùn)行腳本后县貌,產(chǎn)生了一個(gè)hello文件,文件內(nèi)容如下:
wangsheng@ubuntu[18:47:21]:~/Documents$ sh file.sh
wangsheng@ubuntu[18:47:26]:~/Documents$ cat hello
hello linux 1
hello linux 2
hello linux 3
hello linux 4
hello linux 5
如果我只想看文件中的第2行凑懂,就可以用sed命令這么寫:
wangsheng@ubuntu[18:49:47]:~/Documents$ sed -n '2p' hello
hello linux 2
可以看到只顯示了hello文件中的第2行煤痕。這個(gè)例子很簡(jiǎn)單,只是展示一下sed的基本用法接谨,后面會(huì)對(duì)sed的用法進(jìn)行詳細(xì)描述摆碉。這里的-n
是sed提供的一個(gè)選項(xiàng),而'2p'
就是sed的command部分表示打印第二行脓豪;hello
是file部分巷帝,表示操作的是hello這個(gè)文件。
sed的詳細(xì)用法
花樣的命令
命令是sed用法中command部分扫夜,sed之所以擁有強(qiáng)大的功能楞泼,就是因?yàn)槠鋍ommand部分可以組合成非常多的花樣。常用的command通常由兩部分構(gòu)成笤闯,一部分是范圍設(shè)定堕阔,也就是選定行;另一部分是動(dòng)作處理颗味,即對(duì)選定的行執(zhí)行什么樣的操作超陆。
范圍設(shè)定
范圍設(shè)定,顧名思義就是sed命令需要對(duì)哪一行或者哪幾行進(jìn)行操作浦马。值得注意的是时呀,如果不選定行,那么默認(rèn)對(duì)所有行執(zhí)行此操作捐韩。常用的范圍設(shè)定有如下幾種形式:
-
x:x為行號(hào)退唠,表示選定第x行
#顯示第2行 wangsheng@ubuntu[18:49:47]:~/Documents$ sed -n '2p' hello hello linux 2
-
x,y:從第x行到第y行
#顯示2-4行 wangsheng@ubuntu[19:40:50]:~/Documents$ sed -n '2,4p' hello hello linux 2 hello linux 3 hello linux 4
-
x,$:從第x行到最后一行
#顯示第4行到最后一行 wangsheng@ubuntu[19:44:41]:~/Documents$ sed -n '4,$p' hello hello linux 4 hello linux 5
-
x,y!:不包含指定行號(hào)x到y(tǒng)的行
#顯示除了第3行的所有行 wangsheng@ubuntu[19:47:33]:~/Documents$ sed -n '3!p' hello hello linux 1 hello linux 2 hello linux 4 hello linux 5
-
/pattern/:查詢包含該模式的行,這里的
pattern
可以使用正則表達(dá)式荤胁。#查詢顯示含有5的行 wangsheng@ubuntu[19:47:40]:~/Documents$ sed -n '/5/p' hello hello linux 5 #查詢顯示含有l(wèi)inux的行 wangsheng@ubuntu[19:50:47]:~/Documents$ sed -n '/linux/p' hello hello linux 1 hello linux 2 hello linux 3 hello linux 4 hello linux 5
- x,/pattern/ :通過行號(hào)和模式查詢匹配行瞧预,表示從第x行到匹配該模式的行。
動(dòng)作處理
動(dòng)作處理會(huì)提供很多豐富的動(dòng)作供我們選擇仅政,可以利用這些動(dòng)作對(duì)某行內(nèi)容進(jìn)行加入垢油、刪除、修改等操作圆丹。這里介紹幾個(gè)常用的動(dòng)作:
-
p : 打印滩愁,將選中的那行打印輸出到屏幕上。通常
p
會(huì)與參數(shù)sed -n
一起用辫封。(關(guān)于-n
選項(xiàng)的作用硝枉,后面在介紹sed選項(xiàng)的時(shí)候會(huì)詳細(xì)說明)廉丽。#源文件 wangsheng@ubuntu[20:04:00]:~/Documents$ cat hello hello linux 1 hello java 2 hello c++ 3 hello android 4 hello php 5 #顯示第2行 wangsheng@ubuntu[20:04:06]:~/Documents$ sed -n '2p' hello hello java 2 #顯示最后一行 wangsheng@ubuntu[20:05:38]:~/Documents$ sed -n '$p' hello hello php 5 #顯示含有java的行 wangsheng@ubuntu[20:16:25]:~/Documents$ sed -n '/java/p' hello hello java 2
-
a : 增加,a 的后面可以接字符串妻味,而這些字符串會(huì)在新的一行出現(xiàn)(目前的下一行)正压,而如果想添加多行,可以使用
\n
換行符责球。#在文件末添加一行hello python wangsheng@ubuntu[20:08:48]:~/Documents$ sed '$a hello python' hello hello linux 1 hello java 2 hello c++ 3 hello android 4 hello php 5 hello python #使用\n可以添加換行焦履,例如本例在含有android行的下一行添加了兩行內(nèi)容 wangsheng@ubuntu[20:17:39]:~/Documents$ sed '/android/a hello\nworld' hello hello linux 1 hello java 2 hello c++ 3 hello android 4 hello world hello php 5
-
c : 行替換,c 的后面可以接字符串雏逾,這些字符串可以替換 n1,n2 之間的行嘉裤。
#第2行替換為hello world wangsheng@ubuntu[20:09:00]:~/Documents$ sed '2c hello world' hello hello linux 1 hello world hello c++ 3 hello android 4 hello php 5 #第3行至最后一行替換為hello world wangsheng@ubuntu[20:11:06]:~/Documents$ sed '3,$c hello world' hello hello linux 1 hello java 2 hello world
-
d : 刪除行,因?yàn)槭莿h除栖博,所以 d 后面通常不接任何內(nèi)容屑宠,表示刪除選定行的內(nèi)容。
#刪除第1行 wangsheng@ubuntu[20:12:58]:~/Documents$ sed '1d' hello hello java 2 hello c++ 3 hello android 4 hello php 5 #刪除含有java的行 wangsheng@ubuntu[20:19:05]:~/Documents$ sed '/java/d' hello hello linux 1 hello c++ 3 hello android 4 hello php 5
-
s : 替換笛匙,可以直接進(jìn)行搜尋替換的工作侨把。通常 s 可以搭配正則表達(dá)式。
格式:sed 's/要替換的字符串/新的字符串/g'
(要替換的字符串可以用正則表達(dá)式)#搜索含有java的行妹孙,并將該行的java替換成javaee wangsheng@ubuntu[20:19:49]:~/Documents$ sed '/java/s/java/javaee/g' hello hello linux 1 hello javaee 2 hello c++ 3 hello android 4 hello php 5 #也可以使用該功能刪除某個(gè)字符串秋柄,例如本例將java替換成空即刪掉了第2行的java wangsheng@ubuntu[20:25:13]:~/Documents$ sed '/java/s/java//g' hello hello linux 1 hello 2 hello c++ 3 hello android 4 hello php 5
有時(shí)候我們不想在當(dāng)前行的下一行或者上一行添加內(nèi)容,而希望在行首或行尾追加內(nèi)容時(shí)怎么辦呢蠢正?
其實(shí)就可以使用s功能骇笔。其中^表示行首,$表示行尾
#在每行行首添加head------ wangsheng@ubuntu[20:29:56]:~/Documents$ sed 's/^/head------/g' hello head------hello linux 1 head------hello java 2 head------hello c++ 3 head------hello android 4 head------hello php 5 #在每行行尾追加------tail wangsheng@ubuntu[20:35:12]:~/Documents$ sed 's/$/------tail/g' hello hello linux 1------tail hello java 2------tail hello c++ 3------tail hello android 4------tail hello php 5------tail #在第1行行尾追加------tail wangsheng@ubuntu[20:35:36]:~/Documents$ sed '1s/$/------tail/g' hello hello linux 1------tail hello java 2 hello c++ 3 hello android 4 hello php 5
另外嚣崭,如果說s是代表替換笨触,那么末尾的g是什么意思呢?
其實(shí)字符g代表的意思是每行出現(xiàn)的字符全部替換雹舀,如果想在特定字符處添加芦劣,g就有用了,否則只會(huì)替換每行第一個(gè)说榆,而不繼續(xù)往后找了虚吟。
-
r : 文件插入,可以在當(dāng)前行的下一行插入指定文件中的內(nèi)容签财。
#需要插入的文件內(nèi)容 wangsheng@ubuntu[20:28:34]:~/Documents$ cat foo +++this is file in foo+++ #使用a動(dòng)作插入時(shí)串慰,只會(huì)將foo當(dāng)做字符串插入 wangsheng@ubuntu[20:27:46]:~/Documents$ sed '/java/a foo' hello hello linux 1 hello java 2 foo hello c++ 3 hello android 4 hello php 5 #使用r命令插入時(shí),會(huì)將foo代表的文件內(nèi)容插入 wangsheng@ubuntu[20:28:12]:~/Documents$ sed '/java/r foo' hello hello linux 1 hello java 2 +++this is file in foo+++ hello c++ 3 hello android 4 hello php 5
好用的選項(xiàng)
剛才我們已經(jīng)提到了sed的命令的基本構(gòu)成唱蒸,下面來看看sed有哪些好玩的選項(xiàng)邦鲫。
-
-n:使用安靜(silent)模式。在一般 sed 的用法中神汹,所有來自 STDIN的內(nèi)容一般都會(huì)被輸出到屏幕上庆捺。但如果加上 -n 參數(shù)后古今,則只有經(jīng)過sed 特殊處理的那一行(或者動(dòng)作)才會(huì)被輸出出來。
#使用-n選項(xiàng)滔以,只會(huì)輸出處理的那一行 wangsheng@ubuntu[20:43:59]:~/Documents$ sed -n '/java/a ======' hello ====== #不使用-n選項(xiàng)沧卢,輸出所有行 wangsheng@ubuntu[20:44:22]:~/Documents$ sed '/java/a ======' hello hello linux 1 hello java 2 ====== hello c++ 3 hello android 4 hello php 5
-
-e:如果我要執(zhí)行的動(dòng)作包含兩個(gè)或以上怎么辦呢?這也很簡(jiǎn)單醉者,sed的-e屬性就是支持這個(gè)功能的,只要在每個(gè)動(dòng)作之前分別加上-e選項(xiàng)即可披诗。
#前一個(gè)command輸出1-2行撬即,后一個(gè)command輸出第4行 #每個(gè)command之前均加上-e選項(xiàng)就可以同時(shí)處理兩個(gè)動(dòng)作了 wangsheng@ubuntu[20:44:33]:~/Documents$ sed -n -e '1,2p' -e '4p' hello hello linux 1 hello java 2 hello android 4
-
-f:如果設(shè)定的command部分太長(zhǎng),那么可以將command部分內(nèi)容寫到一個(gè)單獨(dú)的文件中呈队,然后使用-f選項(xiàng)來指定這個(gè)文件作為我們sed命令的command部分剥槐。
#已經(jīng)寫好的command,存儲(chǔ)在文件中 wangsheng@ubuntu[20:49:12]:~/Documents$ cat command /java/,/android/p #使用-f選項(xiàng)指定command文件 wangsheng@ubuntu[20:49:16]:~/Documents$ sed -n -f command hello hello java 2 hello c++ 3 hello android 4
-
-i:直接修改讀取的文件內(nèi)容宪摧,而不是輸出到屏幕上粒竖。要注意此選項(xiàng)會(huì)直接改變?cè)次募膬?nèi)容,如果用此命令對(duì)系統(tǒng)配置文件修改的話几于,那么如果出現(xiàn)什么無法挽回的后果也只能怪自己作死了蕊苗。
#源文件 wangsheng@ubuntu[20:49:32]:~/Documents$ cat hello hello linux 1 hello java 2 hello c++ 3 hello android 4 hello php 5 #第2行后添加新行,內(nèi)容為this is a new line wangsheng@ubuntu[20:52:32]:~/Documents$ sed -i '2a this is a new line' hello #源文件被修改 wangsheng@ubuntu[20:53:20]:~/Documents$ cat hello hello linux 1 hello java 2 this is a new line hello c++ 3 hello android 4 hello php 5
sed實(shí)踐——自動(dòng)評(píng)分的腳本
學(xué)了知識(shí)當(dāng)然是為了用的沿彭,那么我們就用一個(gè)實(shí)際的例子來看看sed的應(yīng)用朽砰。
老師在服務(wù)器上為我們每個(gè)人創(chuàng)建了一個(gè)student_ids文件,里面有所有同學(xué)的名單喉刘,每個(gè)同學(xué)都要在該文件中為其他同學(xué)打分瞧柔。為了完成評(píng)分的工作,需要使用vi打開再一個(gè)個(gè)編輯睦裳,而且還不能全打滿分或者有規(guī)律地打分造锅,于是本著能偷懶就偷懶的原則,能不能寫一個(gè)自動(dòng)打分腳本呢廉邑?顯然哥蔚,使用sed這么強(qiáng)大的行處理功能實(shí)現(xiàn)起來肯定是沒問題的。
最終寫出來的腳本如下:
#!/bin/bash
count=0
#統(tǒng)計(jì)行數(shù)
while read line
do
count=$(($count+1))
done < student_ids
for (( i=1; i<="$count"; i++ ))
do
#產(chǎn)生84-99之間的隨機(jī)數(shù)
let score=$RANDOM%15+84
#第i行行尾追加隨機(jī)數(shù)
sed -i "${i}s/$/ $score/g" student_ids
done
首先得到該文件的行數(shù)鬓催,并用一個(gè)變量count記錄下來肺素。然后循環(huán)count次,每次循環(huán)產(chǎn)生一個(gè)84-99之間的隨機(jī)數(shù)宇驾,使用sed命令將此隨機(jī)數(shù)插入到該行的行尾倍靡,然后下一次循環(huán)再對(duì)下一行執(zhí)行同樣的操作。值得注意的是课舍,這里sed使用了-i選項(xiàng)塌西,表示直接對(duì)源文件執(zhí)行此操作他挎,另外使用了強(qiáng)引用“”,而不是弱引用‘’捡需。如果是弱引用的話办桨,那么會(huì)將$score
部分當(dāng)做是一個(gè)字符串,直接在每行行尾加上了$score
這樣的字符串站辉;而如果使用強(qiáng)引用的話呢撞,則直接將$score
代表的隨機(jī)數(shù)插入到行尾。
最后效果如下:
#student_ids源文件
wangsheng@ubuntu[21:03:12]:~/Documents$ cat student_ids
142006010124
142006010125
142006010126
142006010127
142006010128
142006010130
142006010121
142006010122
#執(zhí)行腳本
wangsheng@ubuntu[21:03:27]:~/Documents$ sh autograding.sh
8 lines completed!
#student_ids文件已經(jīng)被修改
wangsheng@ubuntu[21:03:32]:~/Documents$ cat student_ids
142006010124 98
142006010125 90
142006010126 94
142006010127 89
142006010128 96
142006010130 84
142006010121 94
142006010122 98