Linux Shell 高級編程(下)

1.概述

在前面的章節中,我們已經介紹了shell結構化命令,函數用法,正則表達式以及sed,gawk的基本用法,在這裏,主要介紹一下sed和awk的高級用法。sed是流編輯器,具有速度快的優點。sed 每次處理一行數據,在每一行數據上執行腳本命令。而gawk是一種編程語言,能夠對數據進行處理,從而生成數據報告。

2. sed的高級用法

(1)n與N的區別

n: 小寫n告訴sed編輯器移動到數據流中文本的下一行,而不是回到命令的開始。

N: 大寫N是將數據流的下一行添加到模式空間中去。即將數據流的兩個文本行合併,添加到同一模式空間中去。雖然文本行以換行符分隔,但此時流sed編輯器已經將他們作爲同一個文本進行處理了。

 

例子:

 

[root@localhost chapter18]# cat <data1
this is the header line

this is a data line.

this is the last line.

 

 

data1中的數據隔一行有一個空行。

 

 

小寫n:

 

[root@localhost chapter18]# sed '/header/{
n
d
}' data1
this is the header line
this is a data line.

this is the last line.

{}表示在匹配header上執行下面的腳本。

 

即匹配了data1中的第一行,然後n,移動到下一行,d刪除下一行文本,此時就刪除了空行。

 

 

大寫N:

[root@localhost chapter18]# sed '/header/{
N
d
}' data1
this is a data line.

this is the last line.

 

匹配了data1中的第一行,N將下一行添加到模式空間中去,那麼此時流編輯器就把它們作爲一行處理了,雖然它們之間有換行符,所以d就刪除了兩行,分別是第一行與空行。

 

 

有{}與無{}的區別:

[root@localhost chapter18]# sed '/header/n;d;' data1
this is the header line

 

這個命令與上面的命令形式是一樣的,只不過用;將命令分隔開了。

 

首先匹配header,然後下一行,之後一直刪除下一行。所以只留下了第一行。

 

[root@localhost chapter18]# sed '/header/N;d;' data1

如果是N就全刪除了。

 

首先匹配header,然後把下一行添加到模式空間中去,d刪除掉模式空間的內容。之後掃描下一行,N把next添加到模式空間中去......

 

(2) D與d的區別

d是刪除模式空間的當前行。

而D只刪除模式空間的第一行,直至換行符的所有字符,也包括換行符。

 

d      Delete pattern space.  Start next cycle.、

D      Delete up to the first embedded newline  in the  pattern  space.  Start next cycle, but skip reading from the  input   if  there  is still data in the pattern space.

 

區別一下:

d 刪除模式空間,進行下一個循環,即處理下一行。

D刪除模式空間的第一行,如果此時模式空間仍然有數據,強制返回到腳本的開頭,則不處理新的輸入的數據。

 

 

例子1:

[root@localhost chapter18]# cat <data5

This is the header line.
this is the second line.

this is the last line.

 

 

[root@localhost chapter18]# sed '/^$/{
> N
> /heade/D}' data5
This is the header line.
this is the second line.

this is the last line.

 

例子2:

[root@localhost chapter18]# sed '/^$/N;/header/D' data5
this is the second line.

this is the last line.

 

 

這是個比較經典的例子。

分析:

目標: 刪除第一行之前的空行。

 

例子1:

首先通過^$找到空行,然後N將下一行添加到模式空間中去,/header/D 匹配模式空間的內容,所以D就刪除了第一個行,直到換行符爲止。

而此時模式空間還有內容,但由於不是空行,與/^$/不匹配所以就不會執行下面的腳本。

所以最終只刪除了模式空間的第一行。

 

例子2:

前面的過程是一樣的,通過D刪除了第一個行,直到換行符爲止。但由於模式空間中還有內容,必須處理, 與^$不匹配,不執行N,但與/header/D匹配,所以就刪除了第二行。所以header那一行也被刪除了。

 

主要是{}裏面的命令都會執行到前面的模式匹配上。

 

對於D,d,n,N,{},一定要注意區分。

 

 

(3)保留空間與模式空間

模式空間是一個活動緩衝,它在sed編輯器處理命令時保留被檢查的文本。然而,它並不是文本唯一可存儲的可用空間。

而保留空間是一個緩衝區,在處理模式空間的內容時,可以用保留空間暫時保留文本行。

 

sed 保留空間的命令

h  將模式空間的內容複製到保留空間

H  將模式空間的內容追加到保留空間

g  將保留空間的內容複製到模式空間

G  將保留空間的內容追加到模式空間

x  將模式空間的內容與保留空間的內容互換

 

[root@localhost chapter18]# cat <data2
this is the header line.
this is the first data line.
this is the second data line.
this is the last line.

 

[root@localhost chapter18]# sed -n '/first/{
> h
> p
> n
> p
> g
> p
> }' data2
this is the first data line.
this is the second data line.
this is the first data line.

 

分析:

sed -n '/first/  找到first那行。

h   將first那行復制到保留空間中去。

p   打印模式空間的那一行,即first那一行。

n   移動到下一行即second那一行。

p   打印模式空間的行即second.

g   將保留空間的複製到模式空間。

p   即打印first那一行。

 

[root@localhost chapter18]# sed -n '/first/{
h
n
p
g
p
}' data2
this is the second data line.
this is the first data line.

 

先打印了second,再打印了first.

 

(4)反轉整個文本

!表示否定命令。 !p表示不激活命令。

 

[root@localhost chapter18]# sed -n '/header/!p' data2
this is the first data line.
this is the second data line.
this is the last line.

 

即找到header時不打印。

 

 

反轉整個文本:

[root@localhost chapter18]# cat <data2
this is the header line.
this is the first data line.
this is the second data line.
this is the last line

 

 

[root@localhost chapter18]# sed -n '1!G;h;$p' data2
this is the last line.
this is the second data line.
this is the first data line.
this is the header line.

原理:

(1)將一行放到保留空間中

(2)將文本的下一行放到模式空間中去

(3)保留空間追加到模式空間中去

(4)將模式空間放到保留空間中去

(5)重複第2到4,直到所有的以相反的順序放到保留空間

(6)檢索並打印

 

$p表示最後一行纔打印。

 

(5)分支

分支命令允許在數據流的特定子集上執行命令。

[address] b [label]

address決定由哪一行或者是哪些行激發命令。

label決定於何處分支。如果label參數不存在,則分支命令分支到腳本結尾。

 

[root@localhost chapter18]# sed '2,3b;s/this is/is this/' data2
is this the header line.
this is the first data line.
this is the second data line.
is this the last line.

 

 

在2,3行進行分支,由於沒有label,則分支到了腳本的結尾。即跳過了2,3執行替換命令。

 

[root@localhost chapter18]# sed '/first/b jump1;s/ is/might be/;s/line/test/;:jump1 s/data/text/' data2
thismight be the header test.
this is the first text line.
thismight be the second text test.
thismight be the last test.

 

如果匹配first就跳到jump1處執行。如果不匹配就執行3個替換命令,包括jump1後的替換命令。

 

 

[root@localhost chapter18]# echo "this,is,a,test,to,remove,commas."|sed -n ':start s/,/ /p;/,/b start'
this is,a,test,to,remove,commas.
this is a,test,to,remove,commas.
this is a test,to,remove,commas.
this is a test to,remove,commas.
this is a test to remove,commas.
this is a test to remove commas.

 

 

:start 跳到start.

s/,/ /將,替換成空格。

/,/ b start如果有逗號就替換。

即刪除文本中的逗號。

 

 

(6)測試

[address]t [label]

如果替換命令成功匹配了並替換了一個模式,那麼測試命令分支到指定的標籤。

 

如:

 

[root@localhost chapter18]# cat <data2
this is the header line.
this is the first data line.
this is the second data line.
this is the last line.

 

[root@localhost chapter18]# sed 's/first/starting/;t;s/line/test/' data2
this is the header test.
this is the starting data line.
this is the second data test.
this is the last test.

 

找到first並且成功替換,則腳本跳過後續命令。

即第2行的line沒有被替換。

接着腳本讀下一行進行命令處理。所以第三行的被替換了。

 

 

[root@localhost chapter18]# echo "this,is,a,test,to,remove,commas."|sed -n ':start s/,/ /p;t start'
this is,a,test,to,remove,commas.
this is a,test,to,remove,commas.
this is a test,to,remove,commas.
this is a test to,remove,commas.
this is a test to remove,commas.
this is a test to remove commas.

 

如果成功替換了,則繼續執行start.

 

 

(7)模式替換

與號(&)是用來替換命令中的匹配模式,無論什麼樣的匹配模式在命令中都可以用與號來替換它。

 

[root@localhost chapter18]# echo "the cat sleeps in his hat"|sed -n 's/.at/"&"/p'
the "cat" sleeps in his hat

 

即把cat加雙引號。由於沒有g所以後面的hat沒匹配上。

 

sed使用圓括號定義替換模式中的子字符串元素。然後用特定的符號來引用了字符串元素,數字表示子字符串的位置,/1表示第一個圓括號,/2表示第二個圓括號。

 

[root@localhost chapter18]# echo "1234567"|sed ':start s//(.*[0-9]/)/([0-9]/{3/}/)//1,/2/p; t start'
1234,567
1,234,567
1,234,567

 

分析:

t測試前面的替換命令。

.*表示任意個字符。[0-9]以數字結尾。

[0-9]{3}以3數字結尾。

/1表示第一個括號。

/2表示第二個括號。

即把第一個括號與第二個括號中間加上一個逗號。

 

(8)行距

a.雙倍行距

 

[root@localhost chapter18]# sed '$!G' data2
this is the header line.

this is the first data line.

this is the second data line.

this is the last line.

 

 

G是將保留空間的內容追加到模式空間,保留空間的默認值是一個空行。$!G表示最抂行不添加 。

 

b. 對有可能空行的文件使用雙倍行距

先刪除所有的空行,再使用雙倍行距。

[root@localhost chapter18]# cat <data6
This is line one.
This is line two.




This is line three.




This is line four.

 

 

[root@localhost chapter18]# sed '/^$/d;$!G' data6
This is line one.

This is line two.

This is line three.

This is line four.

 

 

(9)對文件中的行計數

=打印行號,N將下一行添加到模式空間,再用空格替換/n即可。

[root@localhost chapter18]# sed '=' data2|sed 'N;s//n/ /'
1 this is the header line.
2 this is the first data line.
3 this is the second data line.
4 this is the last line.

 

(10)打印文件最後幾行

打印文件的最後幾行可以造成滾動的效果。

[root@localhost chapter18]# sed ':start $q;N;5,$D;b start' /etc/passwd
samba1:x:503:503::/home/samba1:/bin/bash
testuser:x:507:507::/home/testuser:/bin/bash
root2:x:508:508::/home/root2:/bin/bash
chenjinzhong:x:509:509::/home/chenjinzhong:/bin/bash

 

:start一直循環,如果是最後一行($),則退出(q).

如果是第5行,D刪除模式空間的第一行。

這樣會顯示最後4行。

 

(11)刪除行

 

a.刪除連續的空行

 

當遇到一個非空行與一個空行則不刪除。

[root@localhost chapter18]# sed '/./,/^$/!d' data6
This is line one.
This is line two.

This is line three.

This is line four

 

b. 刪除開頭的空行

即任意字符到結尾都不會被刪除。

 

[root@localhost chapter18]# cat <data7



this is the first line



this is the second line.
[root@localhost chapter18]# sed '/./,$!d' data7
this is the first line



this is the second line.

 

 

 

c.刪除結尾的空行

 

[root@localhost chapter18]# cat <data8
this is the first line
this is the second line



[root@localhost chapter18]# sed ':start /^/n*$/ {$d;N;b start}' data8
this is the first line
this is the second line

 

 

分析: ^/n*$ 即可能包括一個或多個空行。 /n* 0個或多個。 ^$一個空行。所以整個就是一個或多個空行。

如果是最後一行則刪除$d,如果不是最後一行,則將下一行添加到模式空間中去。

 

3. gawk的高級用法

(1)變量

a. gawk字段分隔符與記錄分隔符。

FIELDWIDTHS  以空格分割的數字列表,用空格定義每個數據字段的精確寬度

FS            輸入字段分割符

RS            輸入記錄分割符

OFS           輸出字段分割符

ORS           輸出記錄分割符

 

[root@localhost chapter19]# cat data1
data11,data12,data13,data14,data15
data21,data22,data23,data24,data25
data31,data32,data33,data34,data35
[root@localhost chapter19]# gawk 'BEGIN {FS=",";OFS="-"} { print $1,$2,$3}' data1
data11-data12-data13
data21-data22-data23
data31-data32-data33

 

FS指定輸入字段分割符,而OFS指定輸出字段分割符。

 

RS記錄輸入分割符。

[root@localhost chapter19]# cat <data2
Riley Mullen
123 Main Street
Chicago,1L 60601
(312)555-1234



Frank Williams
456 Oak Street
Indianapolis, IN 46201
(317)555-9876


Haley Snell
4231 ELM street
Detroit,MI 48201
(313)555-4938
[root@localhost chapter19]# gawk 'BEGIN{FS="/n";RS=""}{printf "%s%s/n",$1,$4}' data2
Riley Mullen(312)555-1234
Frank Williams(317)555-9876
Haley Snell(313)555-4938

 

b.關聯數組

關聯數組使用文本而不是使用數值作爲數組的索引。

 

[root@localhost chapter19]# gawk 'BEGIN{print ENVIRON["PATH"];print ENVIRON["HOME"]}'
/usr/lib/qt-3.3/bin:/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/opt/real/RealPlayer:/root/bin:/opt/real/RealPlayer:/opt/real/RealPlayer:/opt/real/RealPlayer:/opt/real/RealPlayer
/root

 

ENVIRON變量使用關聯數組來檢查shell變量 。

 

[root@localhost chapter19]# gawk 'BEGIN{FS=":";OFS=":"}{print $1,$NF}' /etc/passwd
root:/bin/bash
bin:/sbin/nologin
daemon:/sbin/nologin
adm:/sbin/nologin
lp:/sbin/nologin
sync:/bin/sync
shutdown:/sbin/shutdown
halt:/sbin/halt
mail:/sbin/nologin

 

FS輸入字段分割符爲:,輸出字段分割符也爲:

NF表示最後一個字段的數組,那麼$NF就表示最後一個字段的值。

 

c.用戶自定義變量

 

[root@localhost chapter19]# gawk 'BEGIN{testing="this is a test";print testing;}'
this is a test

定義一個testing變量,然後print輸出。

 

 

[root@localhost chapter19]# cat <script1
BEGIN{FS=","}
{print $n}
[root@localhost chapter19]# gawk -f script1 n=2 data1
data12
data22
data32

 

-f執行腳本。設置變量n的值。但有一個問題,在命令行中設置的n的值在代碼的BEGIN部分不能使用。可以通過-v來解決。

 

 

[root@localhost chapter19]# cat <script2
BEGIN{ print "the starting value is",n;FS=","}
{ print $n }
[root@localhost chapter19]# gawk -f script2 n=3 data1
the starting value is
data13
data23
data33

 

[root@localhost chapter19]# gawk -f script2 -v  n=3 data1
the starting value is 3
data13
data23
data33

這樣在BEGIN部分就能使用了。

 

 

 

(2)數組

定義數組變量:

var[index]=element

 

#!/bin/bash
gawk '
BEGIN{
 var[0]=1
 var[2]=3
 total=var[0]+var[2]
 print total
}'

 

 

(3)使用模式

匹配操作符: ~

#!/bin/bash
gawk '
BEGIN{
FS=":"
}
{
if ($1 ~/root/)
print $1,$NF

}' /etc/passwd

 

$1 ~/root/ 表示第一個字段匹配root.

 

(4)結構化命令

if語句:

 

[root@localhost chapter19]# cat <data4
10
5
13
50
34

 

#!/bin/bash
gawk '{
if($1>20){
x=$1*2
print x
}
}' data4

 

while語句:

 

[root@localhost chapter19]# cat <data5
100 110 120
130 140 150
160 170 180

 

#!/bin/bash
gawk '{
total=0
i=1
while(i<4){
total+=$i
i++

}
avg=total/3
print "Average:",avg

}' data5

 

結果:

[root@localhost chapter19]# ./script8
Average: 110
Average: 140
Average: 170

 

當while計算出第一個記錄的值時,會去讀下一條記錄,然後去計算,記錄是以換行符爲分隔符的。

 

(5)格式化打印

 

printf "format string",var1,var2.....

 

 

#!/bin/bash
gawk '
BEGIN{
FS="/n"
RS=""
}
{
printf "%s-%s/n",$1,$4
}' data2

 

結果是:

 

[root@localhost chapter19]# ./script10
Riley Mullen-(312)555-1234
Frank Williams-(317)555-9876
Haley Snell-(313)555-4938

 

關於awk和sed的高級編程就介紹到這裏了。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章