linux经常用到文本处理,比如sed,awk,grep,而这些又离不开正则表达式。每个都能写一个长篇博客,并且还不一定能写完。
1. sed
sed一般用于单行文本操作,但是这里我只讲多行操作。
给同学改一个网站,把二十多个的html中的公共部分提取出来,做成一个sidebar.html,然后把那二十多个html中相同的代码块替换成include sidebar.html。
sed多行匹配是这样的
sed '/<script>/,/<\/script/p' file
匹配的文本特征为:开始行包含<script>
,结束行包含</script>
的代码块。匹配是非贪婪的,即找到第一个满足条件的就停止。比如如下golang代码段
package main
import "fmt"
func sum(a []int, c chan int) {
sum := 0
for _, v := range a {
sum += v
}
c <- sum
}
func main() {
a := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(a[:len(a)/2], c)
go sum(a[len(a)/2:], c)
x, y := <-c, <-c
fmt.Println(x, y, x+y)
}
sed -n '/func/,/}/p' channels.go
匹配的结果如下
func sum(a []int, c chan int) {
sum := 0
for _, v := range a {
sum += v
}
func main() {
a := []int{7, 2, 8, -9, 4, 0}
而sed -n '/func/,/^}/p' channels.go
的匹配结果如下
func sum(a []int, c chan int) {
sum := 0
for _, v := range a {
sum += v
}
c <- sum
}
func main() {
a := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(a[:len(a)/2], c)
go sum(a[len(a)/2:], c)
x, y := <-c, <-c
fmt.Println(x, y, x+y)
}
另外还可以指定偏移行数来确定代码段,比如下面的命令可以输出文件夹下所有html文件中,从含有<div class="balabala">
的行开始,并且向下6行的代码。
find . -name "*.html" | xargs sed -n '/<div class="balabala">/,+6'p
于是,同学的问题也解决了。
2. awk
以前不知道有pkill,于是我kill到所有包含chrome的进程,都是这么搞的。。
ps aux | grep chromium | awk '{print $2}' | sort -nr | xargs kill -9
awk可以按列处理文本,因此提取中ps的输出的第二列,刚好是pid。
sort -nr使其从大到小排列,因为较大的进程号往往是较小的进程的子进程。
如果先kill小的进程,也就是父进程,那么父进程的子进程也被kill掉了。后面的kill可能会出错,提示找不到进程。如果从子进程开始kill,就不会出现问题。
为了杀个进程也是蛮拼的。。
3. grep
grep一个淫荡的技巧是不输出grep所在的那个进程。
ps aux | grep [c]hroium
原理就是正则表达式”[c]hroium”的语义是字符串”[c]hroium”,但是从形式上二者已经不一样了。
于是grep拿正则表达式”[c]hroium”来匹配字符串”[c]hroium”,二者很明显不匹配。
还有一个选项-o,是只输出匹配的文本,加上正则表达式的断言,可以很精确的提取出所需的文本。
写过一个校园网命令行登录脚本,提取url参数的脚本如下。
echo "<script>top.self.location.href='http://192.168.50.3:8080/eportal/index.jsp?wlanuserip=3d45d9f205c3becb730fdd2b3b232b75&wlanacname=dfa22de185f6a920f90e17e3559e0244&ssid=&nasip=2e9fe26292ae028fc7e9f7d72d6b9a88&mac=ed4a68163130d2ec94707fe1ecab3fb0&t=wireless-v2&url=709db9dc9ce334aad9f3cf534a58c238a63df9f3a78ce15e'</script>" | grep -o -P "(?<=\?).*(?='</)"
输出结果如下
wlanuserip=3d45d9f205c3becb730fdd2b3b232b75&wlanacname=dfa22de185f6a920f90e17e3559e0244&ssid=&nasip=2e9fe26292ae028fc7e9f7d72d6b9a88&mac=ed4a68163130d2ec94707fe1ecab3fb0&t=wireless-v2&url=709db9dc9ce334aad9f3cf534a58c238a63df9f3a78ce15e