批量替换类名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 之后的""表示不生成备份文件,解决报错问题[:>:]

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


用的openwrt路由器,家里宽带申请了动态公网ip,为了方便把2280端口映射到公网,发现经常被暴力破解,自己写了个临时封禁ip功能的脚本,实现5分钟内同一个ip登录密码错误10次就封禁这个ip5分钟,并且进行邮件通知使用步骤openwrt为19.07.03版本,其他版本没有测试过安装bashmsmtpopkg
#!/bin/bashcommand1&command2&wait从Shell脚本并行运行多个程序–杨河老李(kviccn.github.io)
1.先查出MAMP下面集成的PHP版本cd/Applications/MAMP/bin/phpls-ls 2.编辑修改.bash_profile文件(没有.bash_profile文件的情况下回自动创建)sudovim~/.bash_profile在文件的最后输入以下信息,然后保存退出exportPATH="/Applications/MAMP/bin/php/php7.2.20/b
1、先输入locale-a,查看一下现在已安装的语言2、若不存在如zh_CN之类的语言包,进行中文语言包装:apt-getinstalllanguage-pack-zh-hans3、安装好后我们可以进行临时修改:然后添加中文支持: locale-genzh_CN.UTF-8临时修改> export LC_ALL='zh_CN.utf8'> locale永久
BashPerlTclsyntaxdiff1.进制数表示Languagebinaryoctalhexadecimalbash2#[0~1]0[0~7]0x[0~f]or0X[0~f]perl0b[0~1]0[0~7]0x[0~f]tcl0b[0~1]0o[0~7]0x[0~f]bashdifferentbaserepresntationreference2.StringlengthLanguageStr
正常安装了k8s后,使用kubect工具后接的命令不能直接tab补全命令补全方法:yum-yinstallbash-completionsource/usr/share/bash-completion/bash_completionsource<(kubectlcompletionbash)echo"source<(kubectlcompletionbash)">>~/.bashrc 
参考这里启动jar包shell脚本修改过来的#!/bin/bash#默认应用名称defaultAppName='./gadmin'appName=''if[[$1&&$1!=0]]thenappName=$1elseappName=$defaultAppNamefiecho">>>>>>本次重启的应用:$appName<
#一个数字的行#!/bin/bashwhilereadlinedon=`echo$line|sed's/[^0-9]//g'|wc-L`if[$n-eq1]thenecho$linefidone<1.txt#日志切割归档#!/bin/bashcd/data/logslog=1.logmv_log(){[-f$1]&&mv$1$2
#文件增加内容#!/bin/bashn=0cat1.txt|whilereadlinedon=[$n+1]if[$n-eq5]thenecho$lineecho-e"#Thisisatestfile.\n#Testinsertlineintothisfile."elseecho$linefidone#备份/etc目录#
# su - oraclesu: /usr/bin/ksh: No such file or directory根据报错信息:显示无法找到文件 /usr/bin/ksh果然没有该文件,但是发现存在文件/bin/ksh,于是创建了一个软连接,可以规避问题,可以成功切换到用户下,但无法执行系统自带命令。$. .bash_profile-ksh: .: .b
history显示历史指令记录内容,下达历史纪录中的指令主要的使用方法如果你想禁用history,可以将HISTSIZE设置为0:#exportHISTSIZE=0使用HISTIGNORE忽略历史中的特定命令下面的例子,将忽略pwd、ls、ls-ltr等命令:#exportHISTIGNORE=”pwd:ls:ls-ltr:”使用HIS
一.命令历史  1.history环境变量:    HISTSIZE:输出的命令历史条数,如history的记录数    HISTFILESIZE:~/.bash_history保存的命令历史记录数    HISTFILLE:历史记录的文件路径    HISTCONTROL:     ignorespace:忽略以空格开头的命令
之前在网上看到很多师傅们总结的linux反弹shell的一些方法,为了更熟练的去运用这些技术,于是自己花精力查了很多资料去理解这些命令的含义,将研究的成果记录在这里,所谓的反弹shell,指的是我们在自己的机器上开启监听,然后在被攻击者的机器上发送连接请求去连接我们的机器,将被攻击者的she
BashOne-LinersExplained,PartI:Workingwithfileshttps://catonmat.net/bash-one-liners-explained-part-oneBashOne-LinersExplained,PartII:Workingwithstringshttps://catonmat.net/bash-one-liners-explained-part-twoBashOne-LinersExplained,PartII
Shell中变量的作用域:在当前Shell会话中使用,全局变量。在函数内部使用,局部变量。可以在其他Shell会话中使用,环境变量。局部变量:默认情况下函数内的变量也是全局变量#!/bin/bashfunctionfunc(){a=99}funcecho$a输出>>99为了让全局变量变成局部变量
1、多命令顺序执行;  命令1;命令2  多个命令顺序执行,命令之间没有任何逻辑联系&&  命令1&&命令2  逻辑与,当命令1正确执行,才会执行命令2||  命令1||命令2  逻辑或,当命令1执行不正确,才会执行命令2例如:ls;date;cd/home/lsx;pwd;who ddif=输入文件of=输
原博文使用Linux或者unix系统的同学可能都对#!这个符号并不陌生,但是你真的了解它吗?首先,这个符号(#!)的名称,叫做"Shebang"或者"Sha-bang"。Linux执行文件时发现这个格式,会把!后的内容提取出来拼接在脚本文件或路径之前,当作实际执行的命令。 Shebang这个符号通常在Unix系统的脚本
1、历史命令history[选项][历史命令保存文件]选项:-c:  清空历史命令-w:  把缓存中的历史命令写入历史命令保存文件 ~/.bash_historyvim/etc/profile中的Histsize可改存储历史命令数量历史命令的调用使用上、下箭头调用以前的历史命令使用“!n”重复执行第n条历史
目录1.Shell脚本规范2.Shell脚本执行3.Shell脚本变量3.1环境变量3.1.1自定义环境变量3.1.2显示与取消环境变量3.1.3环境变量初始化与对应文件的生效顺序3.2普通变量3.2.1定义本地变量3.2.2shell调用变量3.2.3grep调用变量3.2.4awk调用变量3.3
   http://www.voidcn.com/blog/wszzdanm/article/p-6145895.html命令功能:显示登录用户的信息命令格式:常用选项:举例:w显示已经登录的用户及正在进行的操作[root@localhost~]#w 11:22:01up4days,21:22, 3users, loadaverage:0.00,0.00,0.00USER