介绍寻找满足特定条件的文件路径,简称文件搜索,是shell脚本中的一个常见任务,因为条件复杂多样,这样的任务不简单。很多人都是用find命令来做的,但是find只能覆盖一部分功能,剩下的还需要自己进一步加工,而且find不好用,配合其他部分比较麻烦脚本,而且很容易出错。如果你使用zsh,你基本上不需要find命令。借助zsh自身的功能,足以应对绝大部分场景,而且语法更加优雅简洁,不易出错。一个简单的示例列出了/usr/bin目录中以zsh开头的文件。#添加-l使换行符显示更具可读性。如果需要操作这些文件,用其他命令替换print-l%print-l/usr/bin/zsh*/usr/bin/zsh/usr/bin/zsh-5.4.1/usr/bin/zshdb有人可能会说使用ls/usr/bin/zsh。如果用ls,会增加很多额外的工作,因为zsh已经把文件路径匹配了一次,结果就出来了。传给ls后,ls又去stat那些文件,这完全是多余的工作。如果文件列表很长的话,会花费很多时间。很多你想当然的shell用法都有这样的问题。因此,在键入命令或编写脚本时,不要看结果是否正确,而要知道为什么。如果觉得print-l太长,就aliaspl,print-l很常用。删除/tmp下的所有文件,形式为abc1234.tmp(前字母后数字,数量不限,但至少一字母一数字),包括子目录下的文件。%setoptEXTENDED_GLOB#/**/是一个递归搜索文件###是之前的内容至少重复一次,<->是任意整数或者0%rm-v/tmp/**/[a-zA-Z]##<->.tmpremoved'/tmp/yaourt-tmp-goreliu/abc123.tmp'setoptEXTENDED_GLOB是启用扩展通配符支持。本文以下内容默认此选项。否则,通配符功能太弱了。建议写在.zshrc中。通配符的内容前面已经讲过,可以作为手册参考。两个小例子预热完毕后,我们就开始进入正题。按文件属性搜索除了匹配文件路径外,我们还经常需要按文件属性进行搜索,比如文件类型、权限、大小、修改时间等,这里需要用到一个新东西,通配符修饰符。让我们举个例子看看它是什么样子的。列出当前目录和子目录下的所有普通文件(即ls-l结果中第一个字符为-的文件、非目录、符号链接、设备文件、套接字、FIFO等)。%print-l**/*(.)a.txtb/htm末尾比前面的例子多了一个括号,里面有个点。括号和里面的内容是通配符修饰符,专门用于根据文件属性匹配文件。点(.)代表普通文件。更多例子:#列出当前目录下的非空目录,F表示空%print-l*(/F)#列出当前目录下的空目录,^为否定%print-l*(/^F)#列出当前目录下的符号链接文件和可执行普通文件,多种文件类型用逗号分隔%print-l*(@,.x)#列出符合0644权限的普通文件%print-l*(.f0644)那么让我们看看有哪些通配符修饰符可用,然后举一个更复杂的例子。通配符修饰符列表名称含义用法示例或补充说明/目录F不为空/F(空目录)/^F(非空目录).普通文件@symlink=套接字文件pFIFO文件*可执行普通文件%设备文件%b块设备文件%c字符设备文件r文件有读权限w文件有写权限x文件有执行权限A文件有组用户有读权限I文件有组用户有写权限E文件组用户有执行权限R任意用户有读permissionW任意用户有写权限X任意用户有执行权限ssetuidfileSsetsetgidfiletsetstickybit(粘滞位)文件f符合指定权限f0644f4755f700e待更新内容+待更新内容d指定设备numberl硬连接数l-2(小于2)l+3(大于3)U当前用户拥有G当前用户所在组拥有u指定用户id拥有u1000g指定用户组id拥有g1000a的指定文件的atime说明如下指定文件的mtime说明如下指定文件的ctime说明如下L指定文件大小说明如下UpdateM要更新的内容T要更新的内容N如果没有匹配,返回空无错误D包含隐藏文件(以.开头)n按数值排序,如下所述o升序排序,如下所述O降序排序,如下所述[n]仅获取前n个文件。[5][n1,n2]获取n1到n2个文件/[5,10]:X待更新的内容用法更复杂按文件时间查找文件#列出最后一天修改文件的内容%print-l*(.m-1)#列出最近一个月没有读过的文件%print-l*(.aM+1)m可以在后面加上单位,如果没有单位,默认是天。其他单位:M(月)、w(周)、h(小时)、m(分)、s(秒)。+在指定时间之前,-在指定时间内。a为最后访问时间(atime),但注意,如果挂载分区时指定了noatime或realtime(可以查看/proc/mount确认),那么atime并不是真正的最后访问时间。m为最后修改时间(mtime),指内容修改,不包括文件属性(如权限)的修改。c是最后一次状态修改时间(ctime)。如果文件内容没有被修改,但是文件属性发生了变化,这个时间就会被更新。不明白的请自行上网搜索相关文章。按文件大小查找文件#列出当前目录下小于2k的文件%print-l*(.Lk-2)#列出当前目录下大于1m的文件%print-l*(.Lm+1)#注意这个只能找到空文件,因为如果单位是m,文件只能是0m或者1m,不能是0.5m#所以如果小于1就是0m,就是空文件%print-l*(.Lm-1)default单位为字节,也可以使用k、m、p(512字节块),也可以使用大写的K、M、P,同理意义。文件排序#按文件名排序,同一目录下的文件和目录名会排列在一起,而不是先目录再文件%print-l**/*(.on)bb.txtcc/aa.txtcc/日。txtzz.txt#按照文件的目录深度倒序排列,d为从深到浅,O为倒序%print-l**/*(.Od)zz.txtbb.txtcc/dd.txtcc/aa.txt#先按文件名排序,再按大小排序,这样同样大小的文件还是按文件名排序%print-l**/*(.onoL)bb.txtcc/aa.txtcc/dd.txtcc.txt和第一个例子中的三个一样,可以多次排列。排序可用的因素:n(文件名,如果不指定排序选项,默认按文件名排序,即on),L(大小),l(硬链接数),a(atime),m(mtime),c(ctime),d(目录的深度,从深到浅)。组合使用现在我们大概了解了有哪些通配符修饰符,单独使用也没有问题。但是如果同时使用多个,就涉及到如何组合的问题了。类型和类型必须用逗号分隔。如果不指定类型,则接受所有类型,逗号前后的内容互不干扰(取反^操作只影响逗号前的内容)。同一个类型可以同时添加多个选项,一个一个添加即可。#当前目录下两天内修改过的目录#加上小于3m的普通文件,从小到大依次加上#加上所有符号链接文件(包括隐藏文件)%print-l*(/m-2,.lm-3oL,@D)批量重命名文件批量重命名文件是一个比较常见的场景。Zsh中有一个非常方便的zmv命令,可以使批量重命名变得容易。#使用前需要加载%autoload-Uzmv#将所有txt文件扩展名改为conf#参数要用单引号展开,$1表示第一个参数中括号内的内容%zmv'(*).txt''$1.conf'#如果加上-W参数,zmv会自动识别文件名中需要保留的部分%zmv-W'*.txt''*.conf'#调整文件中各部分的顺序filename%zmv'(*).(*).txt''$2.$1.txt'#添加-n预览而不实际运行%zmv-n'(*).(*).txt''$2.$1.txt'mv--a.b.txtb.a.txt#012...前面加0,使其宽度与101112...%zmv'([0-9]).(*)''0$1.$2'#去掉开头的0%zmv'(0)(*)''$2'#将文件组织到目录中%zmv'(*)-(*)-(*).txt''$1/$2-$3.txt'#转换大小写%zmv'(*).txt''${(U)1}.txt'%zmv'(*).txt''${(L)1}.txt'不展开通配符有时候我们不想展开通配符,比如我写了一个计算函数calc:calc(){zmodloadzsh/mathfuncecho$(($*))}%calc12+1224但如果我想计算12*12:%calc12*12zsh:nomatchesfound:12*12如果不加引号,星号会作为通配符,然后找到匹配12*12的文件名。如果你没有找到所有的文件,你会得到一个错误。但我不是在寻找文件。noglob命令可以禁止后面内容中的通配符展开,这样就不需要引号了。%noglobcalc12*12144然后你可以写一个别名:%aliasjs="noglobcalc"%js12*12144这样你就可以更方便的使用计算器了。小结本文介绍了通配符修饰符在文件搜索中的使用,并列出了最常用的通配符修饰符,以及少量比较复杂或较少使用的临时空缺,以后可能会补上。没必要把这些通配符修饰符全部背下来,熟悉常用的,剩下的用的时候再查。参考http://www.bash2zsh.com/zsh_r...http://blog.sina.com.cn/s/blo...更新历史2017.08.31:增加“不扩展通配符”和“文件批量重命名”。本文不再更新,全系列文章更新维护在这里:github.com/goreliu/zshguideWindows、Linux、Shell、C、C++、AHK、Python、JavaScript、Lua等相关问题的付费解决方案,定价灵活,欢迎咨询,微信ly50247。
