- 論壇徽章:
- 23
|
本帖最后由 ly5066113 于 2010-08-14 12:57 編輯
在shell版混了3年多了,文本編輯方面小有心得,寫出來與大家分享,黑哥等一笑而過。
讀本文之前,需要對sed有一定了解,最好看過sed1line:
http://72891.cn/viewthread.php?tid=336126
本文所有用例的測試環(huán)境采用unix-center的ubuntu服務(wù)器
http://www.unix-center.net/
具體版本如下:- ly5066113@ubuntu:~$ uname -a
- Linux ubuntu 2.6.24-22-generic #1 SMP Mon Nov 24 19:35:06 UTC 2008 x86_64 GNU/Linux
- ly5066113@ubuntu:~$ bash --version
- GNU bash, version 3.2.39(1)-release (x86_64-pc-linux-gnu)
- Copyright (C) 2007 Free Software Foundation, Inc.
- ly5066113@ubuntu:~$ sed --version
- GNU sed version 4.1.5
- Copyright (C) 2003 Free Software Foundation, Inc.
- This is free software; see the source for copying conditions. There is NO
- warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE,
- to the extent permitted by law.
復(fù)制代碼 下面結(jié)合實(shí)例介紹sed應(yīng)用的幾個(gè)方面。
一、標(biāo)簽
b label ,無條件跳轉(zhuǎn)到標(biāo)簽label,如果label沒有指定,跳轉(zhuǎn)到命令的結(jié)尾
t label ,如果最后一次輸入的最后一個(gè) s/// 子命令執(zhí)行成功,跳轉(zhuǎn)到標(biāo)簽label,如果label沒有指定,跳轉(zhuǎn)到命令的結(jié)尾
例1: 用標(biāo)簽完成 是AA就加上YES,不是AA就加NO
http://72891.cn/viewthread.php?tid=1632469
使用t命令:- ly5066113@ubuntu:~$ cat urfile
- AA
- BC
- AA
- CB
- CC
- AA
- ly5066113@ubuntu:~$ sed '/^AA/s/$/ YES/;t;s/$/ NO/' urfile
- AA YES
- BC NO
- AA YES
- CB NO
- CC NO
- AA YES
復(fù)制代碼 如果是AA,執(zhí)行s/$/ YES/,s命令執(zhí)行成功,執(zhí)行t命令,沒有標(biāo)簽,跳轉(zhuǎn)到命令的結(jié)尾,這樣將會(huì)跳過后面的s/$/ NO/
如果不是AA,s/$/ YES/不執(zhí)行,則t命令也不執(zhí)行,只執(zhí)行后面的s/$/ NO/
使用b命令:- ly5066113@ubuntu:~$ sed '/^AA/ba;s/$/ NO/;b;:a;s/$/ YES/' urfile
- AA YES
- BC NO
- AA YES
- CB NO
- CC NO
- AA YES
復(fù)制代碼 如果是AA,執(zhí)行ba,跳轉(zhuǎn)到標(biāo)簽a處,這樣會(huì)跳過中間的s/$/ NO/;b,只執(zhí)行后面的s/$/ YES/
如果不是AA,ba不執(zhí)行,執(zhí)行s/$/ NO/,執(zhí)行b,沒有標(biāo)簽,跳轉(zhuǎn)到命令的結(jié)尾,這樣將會(huì)跳過后面的s/$/ YES/
例2: 合并行:
http://72891.cn/viewthread.php?tid=1381004- ly5066113@ubuntu:~$ cat urfile
- 114.113.144.2:
- 19ms
- 19ms
- 19ms
- 36ms
- 22ms
- 19ms
- 18ms
- 218.61.204.73:
- 0ms
- 0ms
- 0ms
- 0ms
- 0ms
- 0ms
- 0ms
- ly5066113@ubuntu:~$ sed ':a;$!N;/ms$/s/\n/ /;ta;P;D' urfile
- 114.113.144.2: 19ms 19ms 19ms 36ms 22ms 19ms 18ms
- 218.61.204.73: 0ms 0ms 0ms 0ms 0ms 0ms 0ms
復(fù)制代碼 實(shí)現(xiàn)思路:
1、讀入下一行數(shù)據(jù)
2、判斷是否以ms結(jié)尾
3、如果是,替換\n為空格,跳轉(zhuǎn)到1
4、如果不是,打印本行數(shù)據(jù),刪除本行數(shù)據(jù),跳轉(zhuǎn)到1
代碼實(shí)現(xiàn):
:a #定義標(biāo)簽a
$!N #不是最后一行,執(zhí)行N命令
/ms$/s/\n/ / #如果以ms結(jié)尾,將\n替換為空格
ta #如果s///命令執(zhí)行成功,跳轉(zhuǎn)到標(biāo)簽a處
P #打印pattern space的第一行
D #刪除pattern space的第一行,循環(huán)
此代碼是使用sed進(jìn)行合并行操作的典型代碼,對于不同的情況,只需要將/ms$/替換成需要的正則表達(dá)式即可,思路上是通用的。
sed的標(biāo)簽類似于C語言中的goto,cjaizss兄說過,寫代碼的時(shí)候要有狀態(tài)機(jī)的思想,代碼只不過是一種實(shí)現(xiàn)。
下面是他用sed寫的加法,里面應(yīng)用了大量的標(biāo)簽,有興趣可以研究下:
http://72891.cn/viewthread.php?tid=1063437
二、記數(shù)
sed不同于awk,內(nèi)部沒有數(shù)學(xué)運(yùn)算,是否可以實(shí)現(xiàn)記數(shù)器,答案是肯定的,因?yàn)閟ed是圖靈完備的。
例3:如何替換文中第2次和第4次出現(xiàn)的指定字符串
http://bbs3.chinaunix.net/thread-1512243-1-1.html
本例中采用的是這種方案:- sed ':a ; N ; $!ba ;s/root/mmmm/4'
復(fù)制代碼 但這個(gè)方案有2個(gè)弊端:
1、需要將文本一次性讀入到pattern space,如果文件很大,是不行的
2、如果指定字符串在同一行內(nèi)出現(xiàn)多次,那么這個(gè)方法也是不行的
那么是否有其他的替代方案呢?讓我們先從簡單的開始。
將文本中第1次出現(xiàn)a的行替換為b- ly5066113@ubuntu:~$ cat urfile
- a
- a
- a
- a
- a
- a
- ly5066113@ubuntu:~$ sed '0,/a/{s//b/}' urfile
- b
- a
- a
- a
- a
- a
復(fù)制代碼 我們可以利用0,/a/這樣的地址區(qū)間來定位第一次出現(xiàn)a的行。
好,現(xiàn)在提升難度,將文本中第2次出現(xiàn)a的行替換為b- ly5066113@ubuntu:~$ sed '0,/a/b;s/a/b/;ta;b;:a;n;ba' urfile
- a
- b
- a
- a
- a
- a
復(fù)制代碼 繼續(xù),將文本中第3次出現(xiàn)a的行替換為b- ly5066113@ubuntu:~$ sed '0,/a/b;/a/ba;b;:a;n;s/a/b/;tb;ba;:b;n;bb' urfile
- a
- a
- b
- a
- a
- a
復(fù)制代碼 利用0,/a/和標(biāo)簽,也實(shí)現(xiàn)了,但同時(shí)我們發(fā)現(xiàn),隨著次數(shù)的增加,代碼將會(huì)變的越來越煩瑣
是時(shí)候另辟蹊徑了,下面介紹一下我的方法,我為其命名為“打點(diǎn)記數(shù)法”
主要的思路是這樣的:
利用sed的hold space,當(dāng)遇到匹配行時(shí),向hold space里面“打一個(gè).”,使用 . 的個(gè)數(shù)來記錄匹配的次數(shù)
如果 . 的個(gè)數(shù)達(dá)到了要求,則執(zhí)行相應(yīng)的操作- ly5066113@ubuntu:~$ sed '/a/{x;s/^/./;/^.\{3\}$/{x;s/a/b/;b};x}' urfile
- a
- a
- b
- a
- a
- a
- ly5066113@ubuntu:~$ sed '/a/{x;s/^/./;/^.\{4\}$/{x;s/a/b/;b};x}' urfile
- a
- a
- a
- b
- a
- a
- ly5066113@ubuntu:~$ sed '/a/{x;s/^/./;/^.\{5\}$/{x;s/a/b/;b};x}' urfile
- a
- a
- a
- a
- b
- a
復(fù)制代碼 我們可以看到,對于次數(shù)的增加,我們只需要調(diào)整需要匹配的數(shù)值即可。
/a/{ #匹配時(shí),開始記數(shù)
x #交換pattern space與hold space
s/^/./ #向hold space打一個(gè) .
/^.\{3\}$/{ #判斷 . 的個(gè)數(shù)是否達(dá)到要求
x #如果達(dá)到要求,交換hold space與pattern space
s/a/b/ #進(jìn)行替換
b #跳轉(zhuǎn)到代碼結(jié)束
} #
x #交換hold space與pattern space
} #
接下來我們看看“打點(diǎn)記數(shù)法”的另一種應(yīng)用:
例4:提取函數(shù)
http://72891.cn/viewthread.php?tid=1328688- ly5066113@ubuntu:~$ cat urfile
- void
- foo(int a, int b) {
- //impl. skipped
- }
- void bigfunction() {
- int x1, x2, y1, y2, z1, z2;
- // after many lines
- bar(); foo (x1, x2);
- ly5066113@ubuntu:~$ sed -n 'N
- > /foo/{
- > :a
- > /{/{
- > x
- > s/^/./
- > x
- > }
- > /}/{
- > x
- > s/.//
- > /^$/{
- > x
- > p
- > q
- > }
- > x
- > }
- > p
- > n
- > ba
- > }
- > D' urfile
- void
- foo(int a, int b) {
- //impl. skipped
- }
復(fù)制代碼 此代碼的主要思路,就是我在原貼中的回復(fù):
當(dāng)匹配 { 時(shí),x;s/^/./;x ,向hold space里面放一個(gè) .
當(dāng)匹配 } 時(shí),x;s/.//;x ,將hold space里的 . 去掉一個(gè)
就這樣,當(dāng)hold space再次為空時(shí),就表示所有的 { 和 } 都配對了
三、lookup table
在講lookup table技術(shù)之前,先講一下“回溯引用”
回溯引用對于大家來說并不陌生,會(huì)經(jīng)常用于 s/REGEXP/REPLACEMENT/ 中REPLACEMENT部分
但回溯引用并不是只能用于REPLACEMENT部分,在REGEXP中也是可以使用的。
例5:找出100以內(nèi),個(gè)位與十位相同的2位數(shù)- ly5066113@ubuntu:~$ seq 100 | sed -n '/^\(.\)\1$/p'
- 11
- 22
- 33
- 44
- 55
- 66
- 77
- 88
- 99
復(fù)制代碼 我們可以看到,這個(gè)問題利用回溯引用很容易解決。
\1表示前面\(.\)中內(nèi)容,換種說法就是\1與前面\(.\)是相同的,那么就是個(gè)位與十位相同了
在理解了REGEXP中的回溯引用之后,我們來看看lookup table技術(shù)
lookup table就是回溯引用的方式進(jìn)行前后定位,然后取出我們需要內(nèi)容
例6: AIX下怎么用DATE取上月的月份
http://72891.cn/viewthread.php?tid=995655
UNIX下一般都沒有GNU date,即便有GNU date,在某些時(shí)間點(diǎn)(如3月31號)上進(jìn)行取上月操作(-1 month)的時(shí)候也有問題- ly5066113@ubuntu:~$ date +%m
- 08
- ly5066113@ubuntu:~$ date +%m | sed 's/$/b12a01a02a03a04a05a06a07a08a09a10a11a12/;s/^\(..\)b.*\(..\)a\1.*/\2/'
- 07
復(fù)制代碼 我們來看看這段代碼是如何工作的:
1、構(gòu)造一個(gè)列表,字母a左邊的2位數(shù)字是右邊2位數(shù)字的上一個(gè)月
2、利用lookup table取出上一個(gè)月
pattern space初始為:
08
第一個(gè)s命令處理后pattern space變?yōu)椋?br />
08b12a01a02a03a04a05a06a07a08a09a10a11a12
下面我們重點(diǎn)來看看第二個(gè)s命令是怎么工作的:
s/^\(..\)b.*\(..\)a\1.*/\2/
將pattern space里面的內(nèi)容按照上面的正則表達(dá)式進(jìn)行分解
^\(..\) 08
b.* b12a01a02a03a04a05a06a
\(..\) 07
a\1 a08
.* a09a10a11a12
整個(gè)過程就是通過第一個(gè)括號里面的內(nèi)容 08 ,定位到后面的 a08 ,從而取出它前面的2位數(shù)字 07 ,也就是第二個(gè)括號里的內(nèi)容 \2
這種方法就稱之為 lookup table
例7:文本處理
http://72891.cn/viewthread.php?tid=1385995- ly5066113@ubuntu:~$ cat urfile
- 172.27.38.0&1=99&2=100
- 192.168.9.2&1=100&3=111
- 202.96.64.68&1=99&2=1&3=111
- 202.96.69.38&1=99&3=111&4=110
- 202.77.88.99&1=99&2=111&3=66&4=100&5=44
- ly5066113@ubuntu:~$ sed -r 's/&/\n1\n2\n3\n4\n5&/;:a;s/\n(.)(.*)&\1=([^&]+)/\t\3\2/;ta;s/\n./\t0/g' urfile
- 172.27.38.0 99 100 0 0 0
- 192.168.9.2 100 0 111 0 0
- 202.96.64.68 99 1 111 0 0
- 202.96.69.38 99 0 111 110 0
- 202.77.88.99 99 111 66 100 44
復(fù)制代碼 整體思路,用原貼中dream3401的描述:
1、產(chǎn)生1,2,3,4,5的"坐標(biāo)"
2、對每天"有坐標(biāo)的賦值"中的值代入坐標(biāo)
3、對沒有"賦值的坐標(biāo)"代入0
我們以第一行數(shù)據(jù)為例,看看這段代碼是怎么工作的:
pattern space初始為:
172.27.38.0&1=99&2=100
s/&/\n1\n2\n3\n4\n5&/后:
172.27.38.0\n1\n2\n3\n4\n5&1=99&2=100
s/\n(.)(.*)&\1=([^&]+)/\t\3\2/后:
172.27.38.0 99\n2\n3\n4\n5&2=100
s命令執(zhí)行成功,t命令執(zhí)行,跳轉(zhuǎn)到標(biāo)簽a處,再次執(zhí)行s/\n(.)(.*)&\1=([^&]+)/\t\3\2/:
172.27.38.0 99 100\n3\n4\n5
s命令執(zhí)行成功,t命令執(zhí)行,跳轉(zhuǎn)到標(biāo)簽a處,再次執(zhí)行s/\n(.)(.*)&\1=([^&]+)/\t\3\2/,s命令執(zhí)行失敗,無替換
t命令不執(zhí)行,執(zhí)行s/\n./\t0/g:
172.27.38.0 99 100 0 0 0
對于以上步驟,第一個(gè)s命令和最后一個(gè)s命令都不難理解,關(guān)鍵是中間的這句:
s/\n(.)(.*)&\1=([^&]+)/\t\3\2/
那我們以第一次的執(zhí)行為例,將pattern space里面的內(nèi)容按照上面的正則表達(dá)式進(jìn)行分解
\n(.) \n1
(.*) \n2\n3\n4\n5
&\1= &1=
([^&]+) 99
利用第一個(gè)括號的數(shù)字1,定位到后面&1=中的數(shù)字1,從而取出=號后面的數(shù)字99
172.27.38.0 \n1\n2\n3\n4\n5&1=99 &2=100
172.27.38.0 99\n2\n3\n4\n5 &2=100
此正則表達(dá)式在工作的過程中,開頭的 172.27.38.0 和結(jié)尾的 &2=100 都是沒有處理的,處理的只是中間的一部分
例7和例6雖然都是用的lookup table技術(shù),但思路梢有不同。
例6是構(gòu)造一個(gè)列表,然后從列表中l(wèi)ookup出想要的值
例7也是構(gòu)造一個(gè)列表,但是將外面的值填充到列表中
無論是那種方式,lookup table技術(shù)的基本做法都是先構(gòu)造一個(gè)列表,然后用回溯引用定位,從而得到我們需要的值
四、GNU拓展
最后,介紹幾個(gè)GNU sed的拓展功能,個(gè)人認(rèn)為比較實(shí)用:
1、\U \L \u \l
大小寫轉(zhuǎn)換- ly5066113@ubuntu:~$ echo 'abc' | sed 's/.*/\U&/'
- ABC
- ly5066113@ubuntu:~$ echo 'abc' | sed 's/.*/\u&/'
- Abc
- ly5066113@ubuntu:~$ echo 'ABC' | sed 's/.*/\L&/'
- abc
- ly5066113@ubuntu:~$ echo 'ABC' | sed 's/.*/\l&/'
- aBC
復(fù)制代碼 2、/REGEXP/I
正則匹配忽略大小寫- ly5066113@ubuntu:~$ echo 'AbC' | sed -n '/abc/Ip'
- AbC
復(fù)制代碼 3、FIRST~STEP
取出奇數(shù)行或者偶數(shù)行- ly5066113@ubuntu:~$ seq 10 | sed '0~2d'
- 1
- 3
- 5
- 7
- 9
- ly5066113@ubuntu:~$ seq 10 | sed '1~2d'
- 2
- 4
- 6
- 8
復(fù)制代碼 每5行合并成1行,類似 xargs -n5- ly5066113@ubuntu:~$ seq 20 | xargs -n5
- 1 2 3 4 5
- 6 7 8 9 10
- 11 12 13 14 15
- 16 17 18 19 20
- ly5066113@ubuntu:~$ seq 20 | sed ':a;N;s/\n/ /;0~5b;ba'
- 1 2 3 4 5
- 6 7 8 9 10
- 11 12 13 14 15
- 16 17 18 19 20
復(fù)制代碼 4、\%REGEXP%
進(jìn)行路徑匹配時(shí),使用此方法,可以剩去很多轉(zhuǎn)義符\- 16 17 18 19 20
- ly5066113@ubuntu:~$ echo 'a/b/c/d/e/f' | sed -n '/a\/b\/c\/d/p'
- a/b/c/d/e/f
- ly5066113@ubuntu:~$ echo 'a/b/c/d/e/f' | sed -n '\%a/b/c/d%p'
- a/b/c/d/e/f
復(fù)制代碼 5、ADDR1,+N
地址 +行數(shù),可以實(shí)現(xiàn)類似 grep -A N 的功能- ly5066113@ubuntu:~$ seq 10 | grep -A2 '5'
- 5
- 6
- 7
- ly5066113@ubuntu:~$ seq 10 | sed -n '/5/,+2p'
- 5
- 6
- 7
復(fù)制代碼 文筆有限,如果有描述不清的地方請大家見諒。希望本文會(huì)對大家學(xué)習(xí)sed有所幫助。
關(guān)于sed記數(shù),補(bǔ)充幾個(gè)例子
例8:實(shí)現(xiàn) awk '{print NR,$0}' 的功能
來自info sed中的examples(略有改動(dòng))- ly5066113@ubuntu:~$ head sed.info | awk '{print NR,$0}'
- 1 File: sed.info, Node: Top, Next: Introduction, Up: (dir)
- 2
- 3 sed, a stream editor
- 4 ********************
- 5
- 6 This file documents version 4.1.5 of GNU `sed', a stream editor.
- 7
- 8 Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004 Free Software
- 9 Foundation, Inc.
- 10
- ly5066113@ubuntu:~$ head sed.info | sed -nf test.sed
- 1 File: sed.info, Node: Top, Next: Introduction, Up: (dir)
- 2
- 3 sed, a stream editor
- 4 ********************
- 5
- 6 This file documents version 4.1.5 of GNU `sed', a stream editor.
- 7
- 8 Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004 Free Software
- 9 Foundation, Inc.
- 10
- ly5066113@ubuntu:~$ cat test.sed
- #! /usr/bin/sed -f
- x
- 1s/^/1/
- G
- s/\n/ /p
- s/ .*//
- /^9*$/s/^/0/
- s/.9*$/x&/
- h
- s/.*x//
- y/0123456789/1234567890/
- x
- s/x.*//
- G
- s/\n//
- h
復(fù)制代碼 x #交換pattern space與hold space,保存讀入的內(nèi)容
1s/^/1/ #如果是第一行,初始化行號1
G #將保存的內(nèi)容追加回pattern space
s/\n/ /p #將換行替換為空格,并打印
s/ .*// #去處空格以后的所有內(nèi)容,pattern space只剩下行號
/^9*$/s/^/0/ #如果行號都為9,在前面補(bǔ)0
s/.9*$/x&/ #用x分隔不需要改變和需要改變的數(shù)字
h #將pattern space中的內(nèi)容保存到hold space
s/.*x// #刪除不需要改變的數(shù)字
y/0123456789/1234567890/ #對數(shù)字進(jìn)行 +1 的操作
x #交換pattern space與hold space
s/x.*// #刪除需要改變的數(shù)字
G #將改變后的數(shù)字追加回pattern space
s/\n// #刪除換行,得到新的行號
h #保存新行號到hold space
整體的思路:
每讀入一行記錄,將保存在hold space中的行號(如果是第一行,需要初始化),和本行記錄合并輸出
然后將行號 +1 ,保存至hold space
代碼的核心部分就是實(shí)現(xiàn)“行號 +1”,具體做法:
將行號分為2個(gè)部分,一部分保持不變(此部分可能沒有),一部分進(jìn)行改變,然后將需要改變的部分進(jìn)行處理
將處理后的結(jié)果與前面不變的部分拼接在一起,形成新的行號,但這里有個(gè)情況需要考慮,就是進(jìn)位
我們已實(shí)際的例子來看看
如果行號是 123
那么首先將其分成2個(gè)部分變?yōu)?nbsp; 12x3
x左邊的12不需要改變,x右邊的3需要變成4
那么就對3進(jìn)行y/0123456789/1234567890/的操作,將其變成4
然后拼上前面的12變?yōu)?124
如果行號是129(需要進(jìn)位)
那么首先將其分成2個(gè)部分變?yōu)?nbsp; 1x29
x左邊的1不需要改變,x右邊的29需要變成30
那么就對29進(jìn)行y/0123456789/1234567890/的操作,將其變成30
然后拼上前面的1變?yōu)?130
這里面有個(gè)特殊情況需要考慮,就是如果行號都為9
9, 99, 999 之類的情況,那么我們需要在前面加數(shù)字0用做進(jìn)位用
例9:實(shí)現(xiàn) wc -c 的功能
來自info sed中的examples(略有改動(dòng))- ly5066113@ubuntu:~$ wc -c urfile
- 254 urfile
- ly5066113@ubuntu:~$ sed -nf test.sed urfile
- 254
- ly5066113@ubuntu:~$ cat test.sed
- #! /usr/bin/sed -f
- s/./a/g
- H
- x
- s/\n/a/
- : a; s/aaaaaaaaaa/b/g; t b; b done
- : b; s/bbbbbbbbbb/c/g; t c; b done
- : c; s/cccccccccc/d/g; t d; b done
- : d; s/dddddddddd/e/g; t e; b done
- : e; s/eeeeeeeeee/f/g; t f; b done
- : f; s/ffffffffff/g/g; t g; b done
- : g; s/gggggggggg/h/g; t h; b done
- : h; s/hhhhhhhhhh//g
- : done
- $! {
- h
- b
- }
- : loop
- /a/! s/[b-h]*/&0/
- s/aaaaaaaaa/9/
- s/aaaaaaaa/8/
- s/aaaaaaa/7/
- s/aaaaaa/6/
- s/aaaaa/5/
- s/aaaa/4/
- s/aaa/3/
- s/aa/2/
- s/a/1/
- y/bcdefgh/abcdefg/
- /[a-h]/ b loop
- p
復(fù)制代碼 此段代碼雖然看起來煩瑣,但思路卻比上一例好理解。
每讀一行數(shù)據(jù),將里面所有的字符都替換成字母a,因?yàn)閟ed讀數(shù)據(jù)時(shí)會(huì)將換行符(\n)去掉
所以我們利用H命令產(chǎn)生的\n將其補(bǔ)充回來,也替換成字母a,統(tǒng)一做字符統(tǒng)計(jì)
為了節(jié)省內(nèi)存開銷,提高效率,這里做了進(jìn)位的處理,就是將10個(gè)a替換成1個(gè)b,10個(gè)b替換成1個(gè)c 。。。
這樣到最后,字母a的個(gè)數(shù)就代表個(gè)位數(shù)字,字母b的個(gè)數(shù)就代表十位數(shù)字,字母c的個(gè)數(shù)代表百位數(shù)字。。。
如果最后剩下是這樣一串字符:
ccbbbbbaaaa
那么就表示總共的字符數(shù)為:254
本段代碼的統(tǒng)計(jì)是有上限的,如果字符數(shù)量超過1億,將無法得到正確結(jié)果
可以通過增加替換的次數(shù)來增加統(tǒng)計(jì)上限,如 s/hhhhhhhhhh/i/g , s/iiiiiiiiii/j/g 。。。
例10:實(shí)現(xiàn) awk '{sum+=$1}END{print sum}'
http://sed.sourceforge.net/grabbag/scripts/add_decs.sed- ly5066113@ubuntu:~$ seq 100 | awk '{sum+=$1}END{print sum}'
- 5050
- ly5066113@ubuntu:~$ seq 100 | sed -nf test.sed
- 5050
- ly5066113@ubuntu:~$ seq 1000 | awk '{sum+=$1}END{print sum}'
- 500500
- ly5066113@ubuntu:~$ seq 1000 | sed -nf test.sed
- 500500
- ly5066113@ubuntu:~$ cat test.sed
- #! /usr/bin/sed -f
- # This is an alternative approach to summing numbers,
- # which works a digit at a time and hence has unlimited
- # precision. This time it is done with lookup tables,
- # and uses only 10 commands.
- G
- s/\n/-/
- s/$/-/
- s/$/;9aaaaaaaaa98aaaaaaaa87aaaaaaa76aaaaaa65aaaaa54aaaa43aaa32aa21a100/
- :loop
- /^--[^a]/!{
- # Convert next digit from both terms into analog form
- # and put the two groups next to each other
- s/^\([0-9a]*\)\([0-9]\)-\([^-]*\)-\(.*;.*\2\(a*\)\2.*\)/\1-\3-\5\4/
- s/^\([^-]*\)-\([0-9a]*\)\([0-9]\)-\(.*;.*\3\(a*\)\3.*\)/\1-\2-\5\4/
- # Back to decimal, but keeping the carry in analog form
- # \2 matches an `a' if there are at least ten a's, else nothing
- #
- # 1------------- 3- 4----------------------
- # 2 5----
- s/-\(aaaaaaaaa\(a\)\)\{0,1\}\(a*\)\([0-9]*;.*\([0-9]\)\3\5\)/-\2\5\4/
- b loop
- }
- s/^--\([^;]*\);.*/\1/
- h
- $p
復(fù)制代碼 這段代碼很巧妙,先是將需要相加的2個(gè)數(shù)字替換成對應(yīng)的字母a的個(gè)數(shù),然后將字母a合并在一起,
在替換回相加后的結(jié)果數(shù)字,如果超過10就保留一個(gè)a表示進(jìn)位。
例如:123 + 456
那么就先將3和6替換成aaa和aaaaaa,然后合并aaaaaaaaa
這樣在利用lookup table將aaaaaaaaa替換成9,就完成了加法的操作
如果 125 + 456
5和6相加最后就會(huì)變?yōu)閍1,字母a回會(huì)在計(jì)算2加5的時(shí)候一并計(jì)算
這樣也就實(shí)現(xiàn)了進(jìn)位。 |
評分
-
查看全部評分
|