批量替換類名Shell腳本源碼解析

背景

現(xiàn)在做的項(xiàng)目有個(gè)批量修改類名的需求泼诱,包括文件名坛掠、類名、工程文件中的名字治筒。去github上搜了一下還真找到一個(gè)似乎看起來比較滿足需求的腳本:<br /> rename-xcode-files
不過畢竟不能完全滿足自己的需求比如類名的前綴匹配比如屉栓,ATestXXX=>BTestXXX這種形式。
先把這個(gè)腳本的源碼部分貼一下:

#!/bin/bash

1) PROJECT_DIR=.
2) RENAME_CLASSES=rename_classes.txt

#First, we substitute the text in all of the files.
5) sed_cmd=`sed -e 's@^@s/[[:<:]]@; s@[[:space:]]\{1,\}@[[:>:]]/@; s@$@/g;@' ${RENAME_CLASSES} `
6) find ${PROJECT_DIR} -type f \
7) \( -name "*.pbxproj" -or -name "*.h" -or -name "*.m" -or -name "*.xib" -or -name "*.storyboard" \) \
8) -exec sed -i.bak "${sed_cmd}" {} +

# Now, we rename the .h/.m files
11) while read line; do
12)   class_from=`echo $line | sed "s/[[:space:]]\{1,\}.*//"`
13)   class_to=`echo $line | sed "s/.*[[:space:]]\{1,\}//"`
14)   find ${PROJECT_DIR} -type f -regex ".*[[:<:]]${class_from}[[:>:]][^\/]*\.[hm]" -print | egrep -v '.bak$' | \
15)     while read file_from; do
16)       file_to=`echo $file_from | sed "s/\(.*\)[[:<:]]${class_from}[[:>:]]\([^\/]*\)/\1${class_to}\2/"` 
17)       echo mv "${file_from}" "${file_to}"
18)       mv "${file_from}" "${file_to}"
19)     done
20) done < ${RENAME_CLASSES}

其中rename_classes.txt文件內(nèi)容的形式是:

AClass BClass
TestClass TTestClass

源碼解析

雖然算上空行和注釋總共也只有20行代碼耸袜,不過乍一看還是很懵的友多,各種/@:.\等特殊的字符,有些沒有思緒堤框。不過這都是因?yàn)槲覍hell腳本的認(rèn)知其實(shí)還屬于小白階段域滥,所以對一些語法和特性并不理解纵柿,導(dǎo)致想修改腳本滿足自己更多個(gè)性化的需求不知道從何入手。查閱了很多資料启绰,文檔以及請教了一些對Shell腳本非常有經(jīng)驗(yàn)的同事昂儒,終于讀懂了這個(gè)腳本,現(xiàn)在就逐行分析一下源碼委可,算是Mark一下自己這幾天的收獲渊跋。

第1行,第2行
這兩行比較簡單着倾,將當(dāng)前的目錄路徑和當(dāng)前目錄下的rename_classes.txt文件地址賦值給兩個(gè)變量

第5行
sed命令用于對文件以行為單位進(jìn)行內(nèi)容操作拾酝,如增刪改,查找替換卡者。sed命令的語法如下:

sed [選項(xiàng)] '[動(dòng)作]' 文件名
選項(xiàng):
-n:一般sed命令會(huì)把所有數(shù)據(jù)都輸出到屏幕蒿囤,如果加入此選擇則只會(huì)把經(jīng)過sed命令處理的行輸出到屏幕。
-e:允許對輸入數(shù)據(jù)應(yīng)用多條sed命令編輯崇决。
-i:用sed的修改結(jié)果直接修改讀取數(shù)據(jù)的文件材诽,而不是由屏幕輸出。
動(dòng)作:
a:追加嗽桩,在當(dāng)前行后添加一行或多行
c:行替換岳守,用c后面的字符串替換原數(shù)據(jù)行
i:插入凄敢,在當(dāng)前行前插入一行或多行
d:刪除碌冶,刪除指定的行
p:打印,輸出指定的行
s:字串替換涝缝,用一個(gè)字符串替換另外一個(gè)字符串扑庞。格式為“行范圍s/舊字串/新字串/g”,這里/g指的是在整行內(nèi)完整的匹配拒逮,否則默認(rèn)的只是匹配第一次查找到的舊字符串

根據(jù)上面的語法解釋罐氨,sed -e 后面接著的應(yīng)該是多條命令,以;分割滩援。
等同于下面三行命令:

s@^@s/[[:<:]]@;
s@[[:space:]]\{1,\}@[[:>:]]/@;
s@$@/g;@

那么@是什么呢栅隐?
原來,在執(zhí)行替換操作的時(shí)候玩徊,如果要替換的內(nèi)容中包含/租悄,這個(gè)時(shí)候需要對/進(jìn)行了轉(zhuǎn)義成\/,不過這樣表達(dá)式的可讀性必然會(huì)降低恩袱,因此在sed中還可以使用|泣棋,@,^畔塔,!作為命令的分隔符潭辈。

[[:space:]]鸯屿、[[:<:]][[:>:]]又是什么意思呢把敢?
<br /> POSIX Bracket Expressions

從這個(gè)文檔里可以查到寄摆,[:space:]表示空格或者制表符的字符集合,外面那一層[]表示匹配[]中的字符查找技竟,是正則表達(dá)式中的規(guī)則冰肴, ^和$也是正則表達(dá)式中的規(guī)則,分別表示行首和行尾榔组。{1,}是表示匹配次數(shù)的限制熙尉,至少匹配到一次,{m,n}表示匹配到m到n次搓扯。[:<:]和[:>:]查閱很多資料后依然查詢不到检痰,后來在自己的猜測和驗(yàn)證下,這兩個(gè)字符的含義分別是從xx字符開始和以xx字符結(jié)束锨推,其中開始或者結(jié)束的標(biāo)志都是挨著xx字符的不是大寫/小寫字母铅歼、數(shù)字和_
這樣上面三行sed指令就可以翻譯為:

將行首替換為s/[:<:]
將至少匹配到一次的空格字符替換為[:>:]
將行尾替換為/g;

所以

AClass BClass
TestClass TTestClass

執(zhí)行完上面的三行命令后就變成了:

s/[:<:]AClass[:>:]/BClass/g;
s/[:<:]TestClass[:>:]/TTestClass/g;

執(zhí)行到這里看出來了:這幾行命令實(shí)際上最后生成的是一個(gè)新的sed命令,作用是將配置文件中的舊類名替換為新類名换可。個(gè)人認(rèn)為這里的命令行嵌套是此腳本的精髓之處椎椰。

第6行,第7行沾鳄,第8行
find命令可以實(shí)現(xiàn)對于文件的查找慨飘。
find命令的基本組成:

find pathname -options [-print -exec -ok]

參數(shù)
        pathname: find命令所查找的目錄路徑。例如用.來表示當(dāng)前目錄译荞,用/來表示系統(tǒng)根目錄瓤的。
        -print:     find命令將匹配的文件輸出到標(biāo)準(zhǔn)輸出。
        -exec:    find命令對匹配的文件執(zhí)行該參數(shù)所給出的shell命令吞歼。相應(yīng)命令的形式為'command' {} \;圈膏,注意{ }和\;之間的空格篙骡。
        -ok:       和-exec的作用相同稽坤,只不過以一種更為安全的模式來執(zhí)行該參數(shù)所給出的shell命令,在執(zhí)行每一個(gè)命令之前糯俗,都會(huì)給出提示尿褪,讓用戶來確定是否執(zhí)行。

    find命令選項(xiàng)

        -name:按照文件名查找文件叶骨。
        -perm:按照文件權(quán)限來查找文件茫多。
        -prune:使用這一選項(xiàng)可以使find命令不在當(dāng)前指定的目錄中查找,如果同時(shí)使用-depth選項(xiàng)忽刽,那么-prune將被find命令忽略天揖。
        -user: 按照文件屬主來查找文件夺欲。
        -group:按照文件所屬的組來查找文件。

        -mtime -n +n:按照文件的更改時(shí)間來查找文件今膊, - n表示文件更改時(shí)間距現(xiàn)在n天以內(nèi)些阅,+n表示文件更改時(shí)間距現(xiàn)在n天以前。Find命令還有-atime和-ctime選項(xiàng)斑唬,但它們都和-mtime選項(xiàng)市埋。

        -nogroup:查找無有效所屬組的文件,即該文件所屬的組在/etc/groups中不存在恕刘。
        -nouser:查找無有效屬主的文件缤谎,即該文件的屬主在/etc/passwd中不存在。

        -newer file1 ! file2:查找更改時(shí)間比文件file1新但比文件file2舊的文件
        -type 查找某一類型的文件
                b - 塊設(shè)備文件褐着。
                d - 目錄坷澡。
                c - 字符設(shè)備文件。
                p - 管道文件含蓉。
                l - 符號(hào)鏈接文件频敛。
                f - 普通文件

sed -i表示對操作文件的原地修改,sed -i .xxx是可以在原地修改前對操作的文件進(jìn)行備份馅扣,備份后的文件名是原文件名.xxx斟赚。
所以,這幾行命令的意思是:找到當(dāng)前目錄下后綴名為pbxproj,h,m,xib的文件差油,對于每個(gè)找到的文件備份為后綴名為.bak的備份文件拗军,然后再執(zhí)行sed_cm的命令,這樣所有上述格式文件中的舊類名就已經(jīng)被替換成了新類名

第11行厌殉,第20行
讀取文件分為兩步:

1.將文件的內(nèi)容通過重定向(<)的方式傳給while
2.while中調(diào)用read將文件內(nèi)容一行一行的讀出來食绿,并付值給read后跟隨的變量侈咕。變量中就保存了當(dāng)前行中的內(nèi)容公罕。

第12行,第13行
這里的|是管道命令的操作符耀销,"|"只能處理經(jīng)由前面一個(gè)指令傳出的正確輸出信息楼眷,對錯(cuò)誤信息信息沒有直接處理能力。然后熊尉,傳遞給下一個(gè)命令罐柳,作為標(biāo)準(zhǔn)的輸入.
管理命令的輸出說明:

指令1 | 指令2 | 指令3

【指令1】正確輸出,作為【指令2】的輸入狰住,然后【指令2】的輸出作為【指令3】的輸入 张吉,【指令3】輸出就會(huì)直接顯示在屏幕上面了。
通過管道之后【指令1】和【指令2】的正確輸出不顯示在屏幕上面催植。
所以這兩行的意思就非常明顯了肮蛹,讀取rename_classes.txt文件中的舊類名最為class_from勺择,新類名作為class_to

第14行
在當(dāng)前目錄下查找class_from.h或者.m的文件,不包括.bak的備份文件伦忠,也不包括class_from的文件夾目錄

第15行,第19行

while語句
語法:
while 命令/條件
do
語句
done
 
機(jī)制:如果while后的命令執(zhí)行成功省核,或條件真,則執(zhí)行do和done之間的語句昆码,執(zhí)行完成后气忠,再次判斷while后的命令和條件;如果while后的命令執(zhí)行失敗赋咽,或條件為假旧噪,循環(huán)結(jié)束

第16行
\(\)用于匹配文本中的某個(gè)子串。
在sed中脓匿,使用\(\)對匹配的內(nèi)容進(jìn)行分組舌菜,使用\N的方式進(jìn)行引用。示例

echo "Three One Two" | sed 's|\(\w\+\) \(\w\+\) \(\w\+\)|\2 \3 \1|'
One Two Three
我們輸出了Three亦镶,One日月,Two三個(gè)單詞,在sed的替換規(guī)則中缤骨,使用空格分隔了三小段正則表達(dá)式\(\w\+\)來匹配每一個(gè)單詞爱咬,后面使用\1,绊起,\2精拟,\3分別引用它們的值。

所以這里的意思是將XX舊類名YY.h替換成XX新類名YY.h

第17行虱歪,第18行
打印出來改名前的文件路徑和改名后的文件路徑蜂绎,將舊的文件名替換為新的文件名

代碼改進(jìn)

讀懂了源碼之后,就可以修改滿足自己的需求了笋鄙。 目前這個(gè)腳本無法滿足需要的主要是兩個(gè)點(diǎn):
1.備份文件其實(shí)沒必要生成
2.支持對于舊類名的前綴匹配替換

修改方式:
1.第8行-exec sed -i.bak "${sed_cmd}" {} +改為-exec sed -i "" "${sed_cmd}" {} + 师枣,

-i extension
             Edit files in-place, saving backups with the specified extension.
             If a zero-length extension is given, no backup will be saved.  It
             is not recommended to give a zero-length extension when in-place
             editing files, as you risk corruption or partial content in situ-
             ations where disk space is exhausted, etc.

要點(diǎn):
* 用 -i 命令將替換結(jié)果寫入文件
* -i 之后的""表示不生成備份文件,解決報(bào)錯(cuò)問題

2.將原來腳本中的[:>:]都去掉就實(shí)現(xiàn)了前綴匹配

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末萧落,一起剝皮案震驚了整個(gè)濱河市践美,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌找岖,老刑警劉巖陨倡,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異许布,居然都是意外死亡兴革,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門蜜唾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來杂曲,“玉大人箕昭,你說我怎么就攤上這事〗庠模” “怎么了落竹?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長货抄。 經(jīng)常有香客問我述召,道長,這世上最難降的妖魔是什么蟹地? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任积暖,我火速辦了婚禮,結(jié)果婚禮上怪与,老公的妹妹穿的比我還像新娘夺刑。我一直安慰自己,他們只是感情好分别,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布遍愿。 她就那樣靜靜地躺著,像睡著了一般耘斩。 火紅的嫁衣襯著肌膚如雪沼填。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天括授,我揣著相機(jī)與錄音坞笙,去河邊找鬼。 笑死荚虚,一個(gè)胖子當(dāng)著我的面吹牛薛夜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播版述,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼梯澜,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了院水?” 一聲冷哼從身側(cè)響起腊徙,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤简十,失蹤者是張志新(化名)和其女友劉穎檬某,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體螟蝙,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡恢恼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了胰默。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片场斑。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡漓踢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出漏隐,到底是詐尸還是另有隱情喧半,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布青责,位于F島的核電站挺据,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏脖隶。R本人自食惡果不足惜扁耐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望产阱。 院中可真熱鬧婉称,春花似錦、人聲如沸构蹬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽庄敛。三九已至瘫筐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間铐姚,已是汗流浹背策肝。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留隐绵,地道東北人之众。 一個(gè)月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像依许,于是被迫代替她去往敵國和親棺禾。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容