Linux_Shell腳本學習第四章-讓文本飛(上)

一、使用正則表達式

1.1 正則表達式的規則

1.1.1 位置標記

**位置標記錨點(position marker anchor)是標識字符串位置的正則表達式。**默認情況下,正
則表達式所匹配的字符可以出現在字符串中任何位置,如下圖。
在這裏插入圖片描述

1.1.2 標識符

標識符是正則表達式的基礎組成部分。它定義了那些爲了匹配正則表達式,必須存在(或不
存在)的字符,如下圖。
在這裏插入圖片描述

1.1.3 數量修飾符

一個標識符可以出現一次、多次或是不出現。數量修飾符定義了模式可以出現的次數,如下圖。
在這裏插入圖片描述

1.1.4 其他一些特殊字符可以調整正則表達式的匹配方式

在這裏插入圖片描述

1.2 正則表達式的例子

1.2.1 能夠匹配任意單詞的正則表達式

( +[a-zA-Z]+ +)

開頭的+表示需要匹配一個或多個空格。字符組[a-zA-Z]用於匹配所有的大小寫字母。隨後的+
表示至少要匹配一個字母,多者不限。最後的+表示需要匹配一個或多個空格來終結單詞
這個正則表達式無法匹配句子末尾的單詞。

1.2.2 匹配句尾或是逗號前的單詞

( +[a-zA-Z]+[?,.]? +)

[?,.]?表示僅需要匹配問號、逗號或點號中的一個。

1.2.3 匹配ip地址

[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}

[0-9]表示匹配數字。{1,3}表示至少一位數字,至多三位數字:
或者也可以使用[[:digit:]]表示數字:

[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}

1.3 處理特殊字符

正則表達式用$、^、.、*、+、{以及}等作爲特殊字符。但是如果我們希望將這些字符作
爲普通字符使用,應該怎麼做呢?來看一個正則表達式:a.txt。
該正則表達式能夠匹配字符a,然後是任意字符(由.負責匹配),接着是字符串txt。但是我們希望.能夠匹配字面意義上的.,而非任意字符。因此需要在.之前加上一個反斜線\(這叫作“字符轉義”)。這表明正則表達式希望匹配的是字面含義,而不是它所代表的特殊含義。因此,最終的正則表達式就變成了a.txt。

二、使用grep 在文件中搜索文本

2.1 在stdin中搜索匹配特定模式的文本行

$ echo -e "this is a word\nnext line" | grep word
this is a word

2.2 在文件中搜索匹配特定模式的文本行

$ grep pattern filename
this is the line containing pattern

或者

$ grep "pattern" filename
this is the line containing patter

2.3 在多個文件中搜索匹配特定模式的文本行

$ grep "match_text" file1 file2 file3 ...

2.4 選項–color可以在輸出行中着重標記出匹配到的模式。

儘管該選項在命令行中的放置位置沒有強制要求,不過慣常作爲第一個選項出現。

$ grep --color=auto word filename
this is the line containing word

2.5 grep命令默認使用基礎正則表達式

這是先前描述的正則表達式的一個子集。選項-E可以使grep使用擴展正則表達式。也可以使用默認啓用擴展正則表達式的egrep命令

$ grep -E "[a-z]+" filename

或者

$ egrep "[a-z]+" filename

2.6 選項-o可以只輸出匹配到的文本

$ echo this is a line. | egrep -o "[a-z]+\."
line

2.7 選項-v可以打印出不匹配match_pattern的所有行

$ grep -v match_pattern file

2.8 選項-c能夠統計出匹配模式的文本行數

$ grep -c "text" filename
10

需要注意的是-c只是統計匹配行的數量,並不是匹配的次數。例如:

$ echo -e "1 2 3 4\nhello\n5 6" | egrep -c "[0-9]"
2

儘管有6個匹配項,但egrep命令只輸出2,這是因爲只有兩個匹配行。在單行中出現的
多次匹配只被計爲一次。

2.9 統計文件中匹配項的數量

$ echo -e "1 2 3 4\nhello\n5 6" | egrep -o "[0-9]" | wc -l
6

2.10 選項-n可以打印出匹配字符串所在行的行號

$ cat sample1.txt
gnu is not unix
linux is fun
bash is art
$ cat sample2.txt
planetlinux
$ grep linux -n sample1.txt
2:linux is fun

或者

$ cat sample1.txt | grep linux -n

如果涉及多個文件,該選項也會隨輸出結果打印出文件名

$ grep linux -n sample1.txt sample2.txt
sample1.txt:2:linux is fun
sample2.txt:2:planetlinux

2.11 選項-b可以打印出匹配出現在行中的偏移

配合選項-o可以打印出匹配所在的字符或字節偏移。

$ echo gnu is not unix | grep -b -o "not"
7:not

字符在行中的偏移是從0開始計數,不是1。

2.12 選項-l可以列出匹配模式所在的文件

$ grep -l linux sample1.txt sample2.txt
sample1.txt
sample2.txt

和-l效果相反的選項是-L,它會返回一個不匹配的文件列表。

2.13 遞歸搜索多個文件

如果需要在多級目錄中對文本進行遞歸搜索,可以使用下列命令

$ grep "text" . -R -n

命令中的.指定了當前目錄。例如:

$ cd src_dir
$ grep "test_function()" . -R -n
./miscutils/test.c:16:test_function();

等價於

$ find . -type f | xargs grep "test_function()"

2.14 忽略模式中的大小寫

選項-i可以在匹配模式時不考慮字符的大小寫:

$ echo hello world | grep -i "HELLO"
hello

2.15 使用grep匹配多個模式

選項-e可以指定多個匹配模式

$ grep -e "pattern1" -e "pattern2"

上述命令會打印出匹配任意一種模式的行,每個匹配對應一行輸出。例如:

$ echo this is a line of text | grep -o -e "this" -e "line"
this
line

可以將多個模式定義在文件中。選項-f可以讀取文件並使用其中的模式(一個模式一行)

$ grep -f pattern_filesource_filename

例如

$ cat pat_file
hello
cool
$ echo hello this is cool | grep -f pat_file
hello this is cool

2.16 在grep搜索中指定或排除文件

grep可以在搜索過程中使用通配符指定(include)或排除(exclude)某些文件
使用–include選項在目錄中遞歸搜索所有的 .c和 .cpp文件

$ grep "main()" . -r --include *.{c,cpp}

注意,some{string1,string2,string3}會被擴展成somestring1 somestring2 somestring3。

使用選項–exclude在搜索過程中排除所有的README文件:

$ grep "main()" . -r --exclude "README"

選項–exclude-dir可以排除目錄:

$ grep main . -r -exclude-dir CVS

如果需要從文件中讀取排除文件列表,使用–exclude-from FILE。

2.17 使用0值字節後綴的xargs與grep

xargs命令可以爲其他命令提供命令行參數列表。當文件名作爲命令行參數時,建議用0值字節作爲文件名終結符,而非空格。因爲一些文件名中會包含空格字符,一旦它被誤解爲終結符,那麼單個文件名就會被視爲兩個(例如,New file.txt被解析成New和file.txt兩個文件名)。這個問題可以利用0值字節後綴來避免。我們使用xargs從命令(如grep和find)中接收stdin文本。這些命令可以生成帶有0值字節後綴的輸出。爲了指明輸入中的文件名是以0值字節作爲終結,需要在xargs中使用選項-0

創建測試文件:

$ echo "test" > file1
$ echo "cool" > file2
$ echo "test" > file3
$ grep "test" file* -lZ | xargs -0 rm

選項-l告訴grep只輸出有匹配出現的文件名。選項-Z使得grep使用0值字節(\0)作爲文件名的終結符。這兩個選項通常都是配合使用的。xargs的-0選項會使用0值字節作爲輸入的分隔符.

2.18 grep的靜默輸出

有時候,我們並不打算查看匹配的字符串,而只是想知道是否能夠成功匹配。這可以通過設置grep的靜默選項(-q)來實現。在靜默模式中,grep命令不會輸出任何內容。它僅是運行命令,然後根據命令執行成功與否返回退出狀態。0表示匹配成功,非0表示匹配失敗。
下面這個腳本利用grep的靜默模式來測試文件中是否有匹配文本:

  1 #!/bin/bash
  2 
  3 if [ $# -ne 2 ]
  4 then
  5         echo Usage: $0 matah_text filename
  6         exit 1
  7 fi
  8 
  9 match_text=$1
 10 filename=$2
 11 
 12 grep -q "$match_text" $filename
 13 
 14 if [ $? -eq 0 ]
 15 then
 16         echo The text exists in the file
 17 else
 18         echo Text does not exist in the file
 19 fi

這個silent_grep.sh腳本接受兩個命令行參數:一個是需要匹配的單詞(Student),另一個是文
件名(student_data.txt):

$ ./silent_grep.sh Student student_data.txt
The text exists in the file

2.19 打印出匹配文本之前或之後的行

選項-A可以打印匹配結果之後的行

$ seq 10 | grep 5 -A 3
5
6
7
8

選項-B可以打印匹配結果之前的行

$ seq 10 | grep 5 -B 3
2
3
4
5

選項-A和-B可以結合使用,或者也可以使用選項-C,它可以分別打印出匹配結果之前及之後的n行:

$ seq 10 | grep 5 -C 3
2
3
4
5
6
7
8

如果有多個匹配,那麼使用–作爲各部分之間的分隔

$ echo -e "a\nb\nc\na\nb\nc" | grep a -A 1
a
b
--
a
b

三、使用cut 按列切分文件

cut命令能夠提取指定位置或列之間的字符。你可以指定每列的分隔符。在cut的術語中,每列被稱爲一個字段。

3.1 選項-f可以指定要提取的字段

cut -f FIELD_LIST filename

FIELD_LIST是需要顯示的列。它由列號組成,彼此之間用逗號分隔。例如

$ cut -f 2,3 filename

該命令將顯示第2列和第3列。

3.2 cut命令也能夠從stdin中讀取輸入

製表符是字段默認的分隔符。對於沒有使用分隔符的行,會將該行照原樣打印出來。cut的選項-s可以禁止打印出這種行。下面的例子演示瞭如何從使用製表符作爲分隔符的文件中提取列:

$ cat student_data.txt
No Name Mark Percent
1 Sarath 45 90
2 Alex 49 98
3 Anu 45 90

$ cut -f1 student_data.txt
No
1
2
3

3.3 要想提取多個字段,就得給出由逗號分隔的多個字段編號

$ cut -f2,4 student_data.txt
Name   Percent
Sarath 90
Alex   98
Anu    90

3.4 用 --complement選項顯示出沒有被-f指定的那些字段

下面的命令會打印出除第3列之外的所有列

$ cut -f3 --complement student_data.txt
No Name Percent
1 Sarath 90
2 Alex 98
3 Anu 90

3.5 選項-d能夠設置分隔符

下面的命令展示瞭如何使用cut處理由分號分隔的字段

$ cat delimited_data.txt
No;Name;Mark;Percent
1;Sarath;45;90
2;Alex;49;98
3;Anu;45;90

$ cut -f2 -d";" delimited_data.txt
Name
Sarath
Alex
Anu

3.6 指定字段的字符或字節範圍

固定列寬的報表在列與列之間都存在數量不等的空格。你沒法根據字段的位置來提取值,但是可以根據字符位置提取。cut命令可以根據字節或者字符來指定選擇範圍。
除了逗號分隔的列表,cut可以接受下圖列出的記法。
在這裏插入圖片描述
 -b 表示字節
 -c 表示字符
 -f 用於定義字段

例如:

$ cat range_fields.txt
abcdefghijklmnopqrstuvwxyz
abcdefghijklmnopqrstuvwxyz
abcdefghijklmnopqrstuvwxyz
abcdefghijklmnopqrstuvwxy

打印第2個到第5個字符

$ cut -c2-5 range_fields.txt
bcde
bcde
bcde
bcde

打印前2個字符
$ cut -c -2 range_fields.txt
ab
ab
ab
ab
若要用字節作爲計數單位,可以將-c替換成-b。
選項–output-delimiter可以指定輸出分隔符。在顯示多組數據時,該選項尤爲有用:

$ cut range_fields.txt -c1-3,6-9 --output-delimiter ","
abc,fghi
abc,fghi
abc,fghi
abc,fghi

四、使用sed 替換文本

4.1 使用另一個字符串來替換匹配模式

sed可以使用另一個字符串來替換匹配模式。模式可以是簡單的字符串或正則表達式

$ sed 's/pattern/replace_string/' file

或者

$ cat file | sed 's/pattern/replace_string/'

4.1.1 選項-i會使得sed用修改後的數據替換原始文件

$ sed -i 's/text/replace/' file

4.1.2 g標記可以使sed執行全局替換

之前的例子只替換了每行中模式首次匹配的內容

$ sed 's/pattern/replace_string/g' file

/#g標記可以使sed替換第N次出現的匹配:

$ echo thisthisthisthis | sed 's/this/THIS/2g'
thisTHISTHISTHIS
$ echo thisthisthisthis | sed 's/this/THIS/3g'
thisthisTHISTHIS
$ echo thisthisthisthis | sed 's/this/THIS/4g'
thisthisthisTHIS

4.1.3 更改默認的分隔符/

sed命令會將s之後的字符視爲命令分隔符。這允許我們更改默認的分隔符/:

sed 's:text:replace:g'
sed 's|text|replace|g'

如果作爲分隔符的字符出現在模式中,必須使用\對其進行轉義:

sed 's|te\|xt|replace|g'

|是出現在模式中被轉義的分隔符。
將te|xt替換爲replace

4.2 移除空行

空行可以用正則表達式 ^$ 進行匹配
最後的/d告訴sed不執行替換操作,而是直接刪除匹配到的空行

$ sed '/^$/d' file

4.2 直接在文件中替換

如果將文件名傳遞給sed,它會將文件內容輸出到stdout。要是我們想就地(in place)修改文件內容,可以使用選項-i:

$ sed 's/PATTERN/replacement/' -i filename

例如,使用指定的數字替換文件中所有3位數的數字

$ cat sed_data.txt
11 abc 111 this 9 file contains 111 11 88 numbers 0000

$ sed -i 's/\b[0-9]\{3\}\b/NUMBER/g' sed_data.txt
$ cat sed_data.txt
11 abc NUMBER this 9 file contains NUMBER 11 88 numbers 0000

上面的單行命令只替換了所有的3位數字。正則表達式\b[0-9]{3}\b用於匹配3位數字。[0-9]表示數字取值範圍是從0到9。{3}表示匹配之前的數字3次。{3}中的\用於轉義{和}。\b表示單詞邊界。

4.3 已匹配字符串標記(&)

在sed中,我們可以用&指代模式所匹配到的字符串,這樣就能夠在替換字符串時使用已匹配的內容:

$ echo this is an example | sed 's/\w\+/[&]/g'
[this] [is] [an] [example]

在這個例子中,正則表達式\w+匹配每一個單詞,然後我們用[&]替換它。&對應於之前所匹配到的單詞。

4.4 子串匹配標記(\1)

&指代匹配給定模式的字符串。我們還可以使用#來指代出現在括號中的部分正則表達式(注:子模式)所匹配到的內容:

$ echo this is digit 7 in a number | sed 's/digit \([0-9]\)/\1/'
this is 7 in a number

這條命令將digit 7替換爲7。(pattern)用於匹配子串,在本例中匹配到的子串是7。子模式被放入使用反斜線轉義過的()中。對於匹配到的第一個子串,其對應的標記是\1,匹配到的第二個子串是\2,往後以此類推。

$ echo seven EIGHT | sed 's/\([a-z]\+\) \([A-Z]\+\)/\2 \1/'
EIGHT seven

([a-z]+)匹配第一個單詞,([A-Z]+)匹配第二個單詞。\1和\2分別用來引用這兩個單詞。這種引用形式叫作向後引用(back reference)。在替換部分,它們的次序被更改爲\2 \1,因此就呈現出了逆序的結果。

4.5 組合多個表達式

可以利用管道組合多個sed命令,多個模式之間可以用分號分隔,或是使用選項-e PATTERN

sed 'expression' | sed 'expression'

它等同於

$ sed 'expression; expression'

或者

$ sed -e 'expression' -e 'expression'

例如

$ echo abc | sed 's/a/A/' | sed 's/c/C/'
AbC
$ echo abc | sed 's/a/A/;s/c/C/'
AbC
$ echo abc | sed -e 's/a/A/' -e 's/c/C/'
AbC

4.6 引用

sed表達式通常用單引號來引用。不過也可以使用雙引號。shell會在調用sed前先擴展雙引號中的內容。如果想在sed表達式中使用變量,雙引號就能派上用場了。
例如:

$ text=hello
$ echo hello world | sed "s/$text/HELLO/"
HELLO world

$text的求值結果是hello。

五、使用awk 進行高級文本處理

awk命令可以處理數據流。它支持關聯數組、遞歸函數、條件語句等功能。

5.1 awk腳本的結構

awk腳本的結構如下:

awk 'BEGIN{ print "start" } pattern { commands } END{ print "end" }' file

awk命令也可以從stdin中讀取輸入。

awk腳本通常由3部分組成:BEGIN、END和帶模式匹配選項的公共語句塊(common statement
block)。這3個部分都是可選的,可以不用出現在腳本中。
awk以逐行的形式處理文件。BEGIN之後的命令會先於公共語句塊執行。對於匹配PATTERN
的行,awk會對其執行PATTERN之後的命令。最後,在處理完整個文件之後,awk會執行END之後
的命令。

5.2 簡單的awk腳本

簡單的awk腳本可以放在單引號或雙引號中:

awk 'BEGIN { statements } { statements } END { end statements }'

或者

awk "BEGIN { statements } { statements } END { end statements }"

下面的命令會輸出文件行數:

$ awk 'BEGIN { i=0 } { i++ } END { print i}' filename

或者

$ awk "BEGIN { i=0 } { i++ } END { print i }" filename

5.3 awk命令工作方式

5.3.1 首先執行BEGIN { commands } 語句塊中的語句

BEGIN語句塊在awk開始從輸入流中讀取行之前被執行。這是一個可選的語句塊,諸如變量
初始化、打印輸出表格的表頭等語句通常都可以放在BEGIN語句塊中。

5.3.2 接着從文件或stdin中讀取一行,如果能夠匹配pattern,則執行隨後的commands語句塊。重複這個過程,直到文件全部被讀取完畢。

最重要的部分就是和pattern關聯的語句塊。這個語句塊同樣是可選的。如果不提供,則默
認執行{ print },即打印所讀取到的每一行。awk對於讀取到的每一行都會執行該語句塊。這
就像一個用來讀取行的while循環,在循環體中提供了相應的語句。

每讀取一行,awk就會檢查該行是否匹配指定的模式。模式本身可以是正則表達式、條件語
句以及行範圍等。如果當前行匹配該模式,則執行{ }中的語句。

模式是可選的。如果沒有提供模式,那麼awk就認爲所有的行都是匹配的:

5.3.3 當讀至輸入流末尾時,執行END { commands } 語句塊。

END語句塊和BEGIN語句塊類似。它在awk讀取完輸入流中所有的行之後被執行。像打印所有
行的分析結果這種常見任務都是在END語句塊中實現的。

5.3.4 例子

$ echo -e "line1\nline2" | awk 'BEGIN { print "Start" } { print } END { print "End" } '
Start
line1
line2
End

當使用不帶參數的print時,它會打印出當前行。

print能夠接受參數。這些參數以逗號分隔,在打印參數時則以空格作爲參數之間的分隔符。在awk的print語句中,雙引號被當作拼接操作符(concatenation operator)使用。例如:

$ echo | awk '{ var1="v1"; var2="v2"; var3="v3"; print var1,var2,var3 ; }'

該命令輸出如下:

v1 v2 v3

echo命令向標準輸出寫入一行,因此awk的 { } 語句塊中的語句只被執行一次。如果awk的輸入中包含多行,那麼 { } 語句塊中的語句也就會被執行相應的次數。
拼接的使用方法如下:

$ echo | awk '{ var1="v1"; var2="v2"; var3="v3"; print var1 "-" var2 "-" var3 ; }'

該命令輸出如下:

v1-v2-v3

5.4 特殊變量

以下是awk可以使用的一些特殊變量。
 NR:表示記錄編號,當awk將行作爲記錄時,該變量相當於當前行號。
 NF:表示字段數量,在處理當前記錄時,相當於字段數量。默認的字段分隔符是空格。
 $0:該變量包含當前記錄的文本內容。
 $1:該變量包含第一個字段的文本內容。
 $2:該變量包含第二個字段的文本內容。
例如:

$ echo -e "line1 f2 f3\nline2 f4 f5\nline3 f6 f7" | \
awk '{
print "Line no:"NR",No of fields:"NF, "$0="$0,
"$1="$1,"$2="$2,"$3="$3
}'
Line no:1,No of fields:3 $0=line1 f2 f3 $1=line1 $2=f2 $3=f3
Line no:2,No of fields:3 $0=line2 f4 f5 $1=line2 $2=f4 $3=f5
Line no:3,No of fields:3 $0=line3 f6 f7 $1=line3 $2=f6 $3=f7

我們可以用print $NF打印一行中最後一個字段,用 $(NF-1)打印倒數第二個字段,其他字段以此類推。awk也支持printf()函數,其語法和C語言中的同名函數一樣。
下面的命令會打印出每一行的第二和第三個字段:

$awk '{ print $3, $2 }' file

我們可以使用NR統計文件的行數:

$ awk 'END{ print NR }' file

5.5 將外部變量值傳遞給awk

藉助選項-v,我們可以將外部值(並非來自stdin)傳遞給awk:

$ VAR=10000
$ echo | awk -v VARIABLE=$VAR '{ print VARIABLE }'
10000

還有另一種靈活的方法可以將多個外部變量傳遞給awk。例如:

$ var1="Variable1" ; var2="Variable2"
$ echo | awk '{ print v1,v2 }' v1=$var1 v2=$var2
Variable1 Variable2

當輸入來自於文件而非標準輸入時,使用下列命令:

$ awk '{ print v1,v2 }' v1=$var1 v2=$var2 filename

在上面的方法中,變量以鍵-值對的形式給出,使用空格分隔(v1=var1v2=var1 v2=var2),作爲awk的命令行參數緊隨在BEGIN、{}和END語句塊之後。

5.6 用getline讀取行

awk默認讀取文件中的所有行。如果只想讀取某一行,可以使用getline函數。它可以用於在BEGIN語句塊中讀取文件的頭部信息,然後在主語句塊中處理餘下的實際數據

該函數的語法爲:getline var。變量var中包含了特定行。如果調用時不帶參數,我們可以用 $0、$1和$2訪問文本行的內容。例如:

$ seq 5 | awk 'BEGIN { getline; print "Read ahead first line", $0 }
{ print $0 }'
Read ahead first line 1
2
3
4
5

5.7 使用過濾模式對awk處理的行進行過濾

我們可以爲需要處理的行指定一些條件:

$ awk 'NR < 5' # 行號小於5的行
$ awk 'NR==1,NR==4' # 行號在1到5之間的行
$ awk '/linux/' # 包含模式爲linux的行(可以用正則表達式來指定模式)
$ awk '!/linux/' # 不包含模式爲linux的行

5.8 設置字段分隔符

默認的字段分隔符是空格。我們也可以用選項-F指定不同的分隔符

$ awk -F: '{ print $NF }' /etc/passwd

或者

awk 'BEGIN { FS=":" } { print $NF }' /etc/passwd

5.9 從awk中讀取命令輸出

awk可以調用命令並讀取輸出。把命令放入引號中,然後利用管道將命令輸出傳入getline

"command" | getline output ;

下面的代碼從/etc/passwd文件中讀入一行,然後顯示出用戶登錄名及其主目錄。在BEGIN語句塊中將字段分隔符設置爲:,在主語句塊中調用了grep。

$ awk 'BEGIN {FS=":"} { "grep root /etc/passwd" | getline; print $1,$6 }'
root /root

5.10 awk的關聯數組

除了數字和字符串類型的變量,awk還支持關聯數組。關聯數組是一種使用字符串作爲索引的數組。你可以通過中括號中索引的形式來分辨出關聯數組:

arrayName[index]

就像用戶定義的簡單變量一樣,你也可以使用等號爲數組元素賦值:

myarray[index]=value

5.11 在awk中使用循環

在awk中可以使用for循環,其格式與C語言中的差不多:

for(i=0;i<10;i++) { print $i ; }

另外awk還支持列表形式的for循環,也可以顯示出數組的內容:

for(i in array) { print array[i]; }

下面的例子展示瞭如何將收集到的數據存入數組並顯示出來。這個腳本從/etc/password中讀取文本行,以:作爲分隔符將行分割成字段,然後創建一個關聯數組,數組的索引是登錄ID,對應的值是用戶名:

$ awk 'BEGIN {FS=":"} {nam[$1]=$5} END {for {i in nam} {print i,nam[i]}}' /etc/passwd
root root
ftp FTP User
userj Joe User

5.12 awk內建的字符串處理函數

awk有很多內建的字符串處理函數。
 length(string):返回字符串string的長度。
 index(string, search_string):返回search_string在字符串string中出現的位置。
 split(string, array, delimiter):以delimiter作爲分隔符,分割字符串string,將生成的字符串存入數組array。
 substr(string, start-position, end-position) : 返回字符串string 中以start-position和end-position作爲起止位置的子串。
 sub(regex, replacement_str, string):將正則表達式regex匹配到的第一處內容替換成replacment_str。
 gsub(regex, replacement_str, string):和sub()類似。不過該函數會替換正則表達式regex匹配到的所有內容。
 match(regex, string):檢查正則表達式regex是否能夠在字符串string中找到匹配。如果能夠找到,返回非0值;否則,返回0。match()有兩個相關的特殊變量,分別是RSTART和RLENGTH。變量RSTART包含了匹配內容的起始位置,而變量RLENGTH包含了匹配內容的長度。

六、統計特定文件中的詞頻

 1 #!/bin/sh
  2 
  3 if [ $# -ne 1 ]
  4 then
  5         echo "Usage: $0 filename";
  6         exit 1;
  7 fi
  8 
  9 filename=$1
 10 
 11 egrep -o "\b[[:alpha:]]+\b" $filename | \
 12 awk '{count[$0]++} 
 13 END{printf("%-14s\n","Word","Count");
 14         for(i in count)
 15         {
 16                 printf("%-14s%d\n",i,count[i]);
 17         }
 18 }'

egrep命令將文本文件轉換成單詞流,每行一個單詞。模式\b[[:alpha:]]+\b能夠匹配每個單詞並去除空白字符和標點符號。選項-o打印出匹配到的單詞,一行一個。

awk命令統計每個單詞。它針對每一行文本執行{}語句塊中的語句,因此我們不需要再專門爲此寫一個循環。count[$0]++命令負責計數,其中$0是當前行,count是關聯數組。所有的行處理完畢後,END{}語句塊打印出各個單詞及其數量。

六、壓縮或解壓縮JavaScript

我們準備寫一個JavaScript壓縮工具,當然,還包括與之對應的解壓縮工具。來考慮下面Javascript代碼:

$ cat sample.js
function sign_out()
{
	$("#loading").show();
	$.get("log_in",{logout:"True"},
	
	function(){
		window.location="";
	});
}

6.1 壓縮腳本

下面是壓縮JavaScript代碼所需要完成的工作
(1) 移除換行符和製表符。
(2) 移除重複的空格。
(3) 替換掉註釋

$ cat sample.js | \
tr -d '\n\t' | tr -s ' ' \
| sed 's:/\*.*\*/::g' \
| sed 's/ \?\([{}();,:]\) \?/\1/g'

輸出如下

function sign_out(){$("#loading").show();$.get("log_in",
{logout:"True"}, function(){window.location="";});}

(1) tr -d '\n\t’移除\n和\t
(2) tr -s ’ '移除多餘空格
(3)sed 's:/*.**/::g’移除註釋

因爲我們需要使用/* 和*/,所以用:作爲sed的分隔符,這樣就不必對 / 進行轉義了。
*在 sed 中被轉義爲\*。.*用來匹配/*與*/之間所有的文本。

(4) 移除{,}、(,)、;、:以及,前後的空格

sed 's/ \?\([{}();,:]\) \?/\1/g'

上面的sed語句含義如下。
 / ?([{}();,:]) ?/用於匹配,/\1/g 用於替換。
 ([{}();,:])用於匹配字符組[ { }( ) ; , : ](出於可讀性方面的考慮,在這裏加入了空格)中的任意一個字符。(和)是分組操作符,用於記憶所匹配的內容,以便在替換部分中進行向後引用。對(和)轉義之後,它們便具備了另一種特殊的含義,可以作爲分組操作符使用。位於分組操作符前後的?用來匹配可能出現在字符集合周圍的空格。
 在命令的替換部分,匹配的字符串(也就是一個可選的空格、一個來自字符集的字符再加一個可選的空格)被匹配的子串所替換。由分組操作符匹配到並記憶的子串是通過向後引用來指代的。可以用符號\1向後引用該分組所匹配的內容。

6.2 解壓腳本

$ cat obfuscated.txt | sed 's/;/;\n/g; s/{/{\n\n/g; s/}/\n\n}/g'

 s/;/;\n/g 將;替換爲;\n;
 s/{/{\n\n/g 將{替換爲{\n\n;
 s/}/\n\n}/g 將}替換爲\n\n}。

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