批量替换类名Shell脚本源码解析

前端之家收集整理的这篇文章主要介绍了批量替换类名Shell脚本源码解析前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

转载自:http://www.jianshu.com/p/41dff888e330

Git地址:https://github.com/kyle-fox/rename-xcode-files

 
 

背景

现在做的项目有个批量修改类名的需求,包括文件名、类名、工程文件中的名字。去github上搜了一下还真找到一个似乎看起来比较满足需求的脚本: rename-xcode-files 不过毕竟不能完全满足自己的需求比如类名的前缀匹配比如,ATestXXX=>BTestXXX这种形式。 先把这个脚本的源码部分贴一下:


  
  

其中rename_classes.txt文件内容的形式是:


  
  

源码解析

虽然算上空行和注释总共也只有20行代码,不过乍一看还是很懵的,各种/@:.\等特殊的字符,有些没有思绪。不过这都是因为我对shell脚本的认知其实还属于小白阶段,所以对一些语法和特性并不理解,导致想修改脚本满足自己更多个性化的需求不知道从何入手。查阅了很多资料,文档以及请教了一些对Shell脚本非常有经验的同事,终于读懂了这个脚本,现在就逐行分析一下源码,算是Mark一下自己这几天的收获。


这两行比较简单,将当前的目录路径和当前目录下的文件地址赋值给两个变量


sed命令用于对文件以行为单位进行内容操作,如增删改,查找替换。sed命令的语法如下:


  
  

根据上面的语法解释,sed -e 后面接着的应该是多条命令,以;分割。
等同于下面三行命令:


  
  

那么@是什么呢?
原来,在执行替换操作的时候,如果要替换的内容中包含/,这个时候需要对/进行了转义成,不过这样表达式的可读性必然会降低,因此在sed中还可以使用作为命令的分隔符。

、、又是什么意思呢?

POSIX Bracket Expressions

从这个文档里可以查到,[:space:]表示空格或者制表符的字符集合,外面那一层[]表示匹配[]中的字符查找,是正则表达式中的规则, ^和$也是正则表达式中的规则,分别表示行首和行尾。{1,}是表示匹配次数的限制,至少匹配到一次,{m,n}表示匹配到m到n次。[:<:]和[:>:]查阅很多资料后依然查询不到,后来在自己的猜测和验证下,这两个字符的含义分别是从xx字符开始和以xx字符结束,其中开始或者结束的标志都是挨着xx字符的不是大写/小写字母、数字和_
这样上面三行sed指令就可以翻译为:


  
  

所以


  
  

执行完上面的三行命令后就变成了:


  
  

执行到这里看出来了:这几行命令实际上最后生成的是一个新的sed命令,作用是将配置文件中的旧类名替换为新类名。个人认为这里的命令行嵌套是此脚本的精髓之处。


find命令可以实现对于文件的查找。
find命令的基本组成:


  
  

sed -i表示对操作文件的原地修改,sed -i .xxx是可以在原地修改前对操作的文件进行备份,备份后的文件名是原文件名.xxx。
所以,这几行命令的意思是:找到当前目录下后缀名为pbxproj,h,m,xib的文件,对于每个找到的文件备份为后缀名为.bak的备份文件,然后再执行sed_cm的命令,这样所有上述格式文件中的旧类名就已经被替换成了新类名


读取文件分为两步:


  
  


这里的|是管道命令的操作符,"|"只能处理经由前面一个指令传出的正确输出信息,对错误信息信息没有直接处理能力。然后,传递给下一个命令,作为标准的输入.
管理命令的输出说明:


  
  

【指令1】正确输出,作为【指令2】的输入,然后【指令2】的输出作为【指令3】的输入 ,【指令3】输出就会直接显示在屏幕上面了。
通过管道之后【指令1】和【指令2】的正确输出不显示在屏幕上面。
所以这两行的意思就非常明显了,读取rename_classes.txt文件中的旧类名最为class_from,新类名作为class_to


在当前目录下查找class_from.h或者.m的文件,不包括.bak的备份文件,也不包括class_from的文件夹目录


  
  


用于匹配文本中的某个子串。
在sed中,使用对匹配的内容进行分组,使用\N的方式进行引用。示例


  
  

所以这里的意思是将XX旧类名YY.h替换成XX新类名YY.h


打印出来改名前的文件路径和改名后的文件路径,将旧的文件名替换为新的文件

代码改进

读懂了源码之后,就可以修改满足自己的需求了。 目前这个脚本无法满足需要的主要是两个点:
1.备份文件其实没必要生成
2.支持对于旧类名的前缀匹配替换

修改方式:
1.第8行改为 ,


  
  

2.将原来脚本中的都去掉就实现了前缀匹配

作者:东野浪子链接:http://www.jianshu.com/p/41dff888e330來源:简书著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。#!/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}AClass BClass TestClass TTestClass第1行,第2行rename_classes.txt第5行sed [选项] '[动作]' 文件名 选项: -n:一般sed命令会把所有数据都输出到屏幕,如果加入此选择则只会把经过sed命令处理的行输出到屏幕。 -e:允许对输入数据应用多条sed命令编辑。 -i:用sed的修改结果直接修改读取数据的文件,而不是由屏幕输出。 动作: a:追加,在当前行后添加一行或多行 c:行替换,用c后面的字符串替换原数据行 i:插入,在当前行前插入一行或多行 d:删除删除指定的行 p:打印,输出指定的行 s:字串替换,用一个字符串替换另外一个字符串。格式为“行范围s/旧字串/新字串/g”,这里/g指的是在整行内完整的匹配,否则默认的只是匹配第一次查找到的旧字符串s@^@s/[[:<:]]@; s@[[:space:]]\{1,\}@[[:>:]]/@; s@$@/g;@\/|,@,^,![[:space:]][[:<:]][[:>:]]将行首替换为s/[:<:] 将至少匹配到一次的空格字符替换为[:>:] 将行尾替换为/g;AClass BClass TestClass TTestClasss/[:<:]AClass[:>:]/BClass/g; s/[:<:]TestClass[:>:]/TTestClass/g;第6行,第7行,第8行find pathname -options [-print -exec -ok] 参数 pathname: find命令所查找的目录路径。例如用.来表示当前目录,用/来表示系统根目录。 -print: find命令将匹配的文件输出到标准输出。 -exec: find命令对匹配的文件执行该参数所给出的shell命令。相应命令的形式为'command' {} \;,注意{ }和\;之间的空格。 -ok: 和-exec的作用相同,只不过以一种更为安全的模式来执行该参数所给出的shell命令,在执行每一个命令之前,都会给出提示,让用户来确定是否执行。 find命令选项 -name:按照文件名查找文件。 -perm:按照文件权限来查找文件。 -prune:使用这一选项可以使find命令不在当前指定的目录中查找,如果同时使用-depth选项,那么-prune将被find命令忽略。 -user: 按照文件属主来查找文件。 -group:按照文件所属的组来查找文件。 -mtime -n +n:按照文件的更改时间来查找文件, - n表示文件更改时间距现在n天以内,+n表示文件更改时间距现在n天以前。Find命令还有-atime和-ctime选项,但它们都和-mtime选项。 -nogroup:查找无有效所属组的文件,即该文件所属的组在/etc/groups中不存在。 -nouser:查找无有效属主的文件,即该文件的属主在/etc/passwd中不存在。 -newer file1 ! file2:查找更改时间比文件file1新但比文件file2旧的文件 -type 查找某一类型的文件 b - 块设备文件。 d - 目录。 c - 字符设备文件。 p - 管道文件。 l - 符号链接文件。 f - 普通文件第11行,第20行1.将文件内容通过重定向(<)的方式传给while 2.while中调用read将文件内容一行一行的读出来,并付值给read后跟随的变量。变量中就保存了当前行中的内容。第12行,第13行指令1 | 指令2 | 指令3第14行第15行,第19行while语句 语法: while 命令/条件 do 语句 done 机制:如果while后的命令执行成功,或条件真,则执行do和done之间的语句,执行完成后,再次判断while后的命令和条件;如果while后的命令执行失败,或条件为假,循环结束第16行\(\)\(\)echo "Three One Two" | sed 's|\(\w\+\) \(\w\+\) \(\w\+\)|\2 \3 \1|' One Two Three 我们输出了Three,One,Two三个单词,在sed的替换规则中,使用空格分隔了三小段正则表达式\(\w\+\)来匹配每一个单词,后面使用\1,,\2,\3分别引用它们的值。第17行,第18行-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. 要点: * 用 -i 命令将替换结果写入文件 * -i 之后的""表示不生成备份文件解决报错问题[:>:]
原文链接:https://www.f2er.com/bash/390031.html

猜你在找的Bash相关文章