Wildcard与Regular Expression的区别

转载自 Shell十三问:http://wiki.jikexueyuan.com/project/13-questions-of-shell/wildcard.html

题目:[^ ] 跟[! ]差在哪? (wildcard)

这个题目说穿了, 就是要探讨Wildcard与Regular Expression的差别的。 这也是很多初学shell的朋友很容易混淆的地方。

首先,让我们回到十三问之第2问, 再一次将我们提到的command line format 温习一次:

command_nameoptionsarguments

同时,也再来理解一下,我在第5章所提到的变量替换的特性:

先替换,再重组commandline!

有了这个两个基础后,再让我们来看Wildcard是什么回事吧。

Part-I Wildcard (通配符)

首先,

`Wildcard`也是属于`commandline`的处理工序,作用于`arguments`里的`path`之上。

没错,它不用在command_name,也不用在options上。 而且,若argument不是path的话,那也与wildcard无关。

换句更为精确的定义来讲,

`wildcard`是一种命令行的路径扩展(pathexpansion)功能。

提到这个扩展,那就不要忘了 command line的“重组”特性了!

是的,这与变量替换(variable subtitution)及命令替换(command substitution)的重组特性是一样的。

也就是在wildcard进行扩展后, 命令行会先完成重组,才会交给shell来处理。

了解了wildcard的扩展与重组特性后, 接下来,让我们了解一些常见的wildcard吧。

wildcard 功能
* 匹配0个或多个字符
? 匹配任意单一字符
[list] 匹配list中任意单一字符
[!list] 匹配不在list中任意单一字符
{string1,string2,...} 匹配string1或者stsring2或者(...)中其一字符串

Note: list 中可以指定单个字符,如abcd,也可以指定ASCII字符的起止范围,如 a-d。 即[abcd] 与 [a-d] 是等价的,称为一个自定义的字符类。

例如:

a*b#a与b之间可以有任意个字符(0个或多个),如aabcb,axyzb,a012b,ab等。
a?b#a与b之间只能有一个字符,但该字符可以任意字符,如aab,abb,acb,azb等。
a[xyz]b#a与b之间只能有一个字符,但这个字符只能是x或者y或者z,如:axb,ayb,azb这三个。
a[!0-9]b#a与b之间只能有一个字符,但这个字符不能是阿拉伯数字,如aab,ayb,a-b等。
a{abc,xyz,123}b#a与b之间只能是abc或者xyz或者123这三个字串之一,扩展后是aabcb,axyzb,a123b。
  1. [! ]中的!只有放在第一位时,才有取反的功效。 eg:[!a]*表示当前目录下不以a开头的路径名称;/tmp/[a\!]*表示/tmp目录下所有以a 或者 ! 开头的路径名称;

    思考:为何!前面要加\呢?提示是十三问之4.

  2. [ - ]中-左右两边均有字符时,才表示一个范围,否则,仅作-(减号)字符来处理。 举例:/tmp/*[-z]/[a-zA-Z]*表示/tmp 目录下所有以z或者-结尾的子目录中, 以英文字母(不分大小写)开头的目录名称。

  3. 以*或?开头的wildcard不能匹配隐藏文件(即以.开头的文件名)。 eg:*.txt并不能匹配.txt但能匹配1.txt这样的路径名。 但1*txt及1?txt均可匹配1.txt这样的路径名。

基本上,要掌握wildcard并不难, 只要多加练习,再勤于思考,就能灵活运用了。

再次提醒:

别忘了wildcard的"扩展"+"重组"这个重要特性,而且只作用在argument的path上。

比方说, 假如当前目录下有: a.txt b.txt c.txt 1.txt 2.txt 3.txt 这几个文件。

当我们在命令行中执行ls -l [0-9].txt的命令行时, 因为wildcard处于argument的位置上,

于是根据匹配的路径,扩展为: 1.txt 2.txt 3.txt, 在重组出ls -l 1.txt 2.txt 3.txt这样的命令行。

因此,你在命令行上敲ls -l [0-9].txtls -l 1.txt 2.txt 3.txt输出的结果是一样, 原因就是在于此。


Part-II Regular Expression (正则表达式)

接下来的Regular Expression(RE) 可是个大题目,要讲的很多。 我这里当然不可能讲得很全。 只希望能带给大家一个基本的入门概念,就很足够了...

先来考一下英文好了:What is expression? 简单来说,就是"表达",也就是人们在沟通的时候所要陈述的内容。

然而,生活中,表达方要清楚的将意思描述清楚, 而让接收方完整无误地领会,可不是件容易的事情。

因而才会出现那么多的"误会",真可叹句"表达不易"啊......

同样的情形也发生在计算机的数据处理过程中, 尤其是当我们在描述一段"文字内容"的时候.... 那么,我们不禁要问: 有何方法可以让大家的误会降至最低程度, 而让表达的精确度达到最高程度呢? 答案就是"标准化"了, 也就是我们这里要谈的Regular Expression啦...^_^

然而,在进入RE介绍之前,不妨先让我们温习一下shell十三问之第4问, 那就是关于quoting的部分。

关键是要能够区分 shell command line上的meta与literal的这两种不同的字符类型

然后,我这里也跟你讲:RE 表达式里字符也分meta与literal这两种

呵,不知亲爱的读者是否被我搞混乱了呢?... ^_^

这也难怪啦,因为这的确是最容易混淆的地方, 刚学RE的朋友很多时候,都死在这里! 因此,请特别小心理解哦...

简单而言,除非你将RE写在特定程序使用的脚本里, 否则,我们的RE也是通过 command line输入的。 然而,不少RE所使用的meta字符,跟shell 的meta字符是冲突的

比方说, *``这个字符,在RE里是一个modifier(修饰符);而在command line上,确是wildcard(通配符)**。

那么,我们该如何解决这样的冲突呢? 关键就是看你对shell十三问的第4问中所提的quoting是否足够理解了!

若你明白到shell quoting 就是用来在command line上关闭shell meta这一基本原理, 那你就能很轻松的解决 RE meta与shell meta的冲突问题了:用shell quoting 关闭掉shell meta就是了。 就这么简单... ^_^

再以刚提到*字符为例, 若在command line的path中没有quoting处理的话, 如abc* 就会被作为wildcard expression来扩充及重组了。 若将其置于quoting中,即"abc*",则可以避免wildcard expand的处理。

好了,说了大半天,还没有进入正式的RE介绍呢.... 大家别急,因为我的教学风格就是要先建立基础,循序渐进的... ^_^ 因此,我这里还要再��嗦一个观念,才会到RE的说明啦...(哈...别打我...)

当我们在谈到RE时,千万别跟wildcard搞混在一起! 尤其是

在commandline的位置里,wildcard只作用于argument的path上;
而RE却只用于"字符串处理"的程序中,这与路径名一点关系也没有。

Tips:RE 所处理的字符串,通常是指纯文本或通过stdin读进的内容。

okay,够了够了,我已看到一堆人开始出现不耐烦的样子了... ^_^ 现在,就让我们登堂入室,揭开RE的神秘面纱吧, 这样可以放过我了吧? 哈哈...

在RE的表达式里,主要分为两种字符:literalmeta。 所谓literal就是在RE里不具有特殊功能的字符,如abc,123等; 而meta,在RE里具有特殊的功能。 要关闭之,需要在meta之前使用escape()转义字符。

然而,在介绍meta之前,先让我们来认识一下字符组合(character set)会更好些。

一、所谓的char set就是将多个连续的字符作为一个集合。 例如:

char set
意义
abc 表示abc三个连续的字符,但彼此独立而非集合。(可简单视为三个char set)
(abc) 表示abc这三个连续字符的集合。(可简单视为一个char set)
abc|xyz 表示abc或xyz这两个char set之一
[abc] 表示单一字符,可为a或b或c;与wildcard的[abc]原理相同,称之为字符类。
[^abc] 表示单一字符,不为a或b或c即可。(与wildcard [!abc]原理相同)
. 表示任意单个字符,(与wildcard的?原理相同)

note: abc|xyz 表示abc或xyz这两个char set之一

在认识了RE的char set这个概念之后,然后,在让我们多认识几个RE中常见的meta字符:

二、 锚点(anchor): 用以标识RE在句子中的位置所在。 常见的有:

锚点
说明
^ 表示句首。如,^abc表示以abc开头的句子。
$ 表示句尾。如,abc$表示以abc结尾的句子。
\< 表示词首。如,\<abc表示以abc开头的词。
\> 表示词尾。如,abc\>表示以abc结尾的词。

三、 修饰符(modifier):独立表示时本身不具意义,专门用以修饰前一个char set出现的次数。 常见的有:

modifier表示前一个char set至少出现n次,至多出现m次。如ab{n,m}c 表示a与c之间至少有n个b,至多有m个b。
* 表示前一个char set出现0次或多次,即任意次。如ab*c表示a与c之间可以有0个或多个b。
? 表示前一个char set出现0次或1次,即至多出现1次。如ab?c 表示a与c之间可以有0个或1个b。
+ 表示前一个char set出现1次或多次,即至少出现1次。如ab+c 表示a与c之间可以有1个或多个b。
{n} 表示前一个char set出现n次。如ab{n}c 表示a与c之间可以有n个b。
{n,} 表示前一个char set至少出现n次。如ab{n}c 表示a与c之间至少有n个b。

然而,当我们在识别modifier时,却很容易忽略"边界(boundary)字符"的重要性。

ab{3,5}c为例,这里的a与c就是边界字符了。 若没有边界字符的帮忙,我们很容易做出错误的解读。 比方说: 我们用

然而,答案却是可以的!为什么呢? 让我们重新解读同样,我们用b{3,5}c也同样可以抓到"abbbbbbbbbbc" 这样的字符串。

但当我们用有空在思考一下,为何我们用下面这些RE都抓到abc这样的字符串呢?

x*
ax*,abx*,ax*b
abcx*,abx*c,ax*bc
bx*c,bcx*,x*bc

但,若我们在这些RE前后分别加^$这样的anchor,那又如何呢?

刚学RE时,只要能掌握上面这些基本的meta的大概就可以入门了。 一如前述,RE是一种规范化的文字表达式, 主要用于某些文字处理工具之间,如: grep, perl, vi,awk,sed,等等, 常用于表示一段连续的字符串,查找和替换。

然而每种工具对RE表达式的具体解读或有一些细微差别, 不过节本原理还是一致的。 只要掌握RE的基本原理,那就一理通百理了, 只是在实践时,稍加变通即可。

比方以grep来说, 在Linux上,你可以找到grep,egrep,fgrep这些程序, 其差异大致如下:

grep: 传统的grep程序,在没有任何选项(options)的情况下,只输出符合RE字串的句子, 其常见的选项如下:

选项 (option)
用途
-v 反模式, 只输出“不含”RE的字符串的行。
-r  递归模式,可同时处理所有层级的子目录里的文件
-q 静默模式,不输出任何结果(stderr 除外,常用于获取return value,符合为true,否则,为false.
-i 忽略大小写
-w 整词匹配,类似 \<RE>
-n 同时输出行号
-l 输出匹配RE的文件名
-o 只输出匹配RE的字符串。(gnu新版独有,不见得所有版本支持)
-E 切换为egrep

egrep:为grep的扩充版本,改良了许多传统grep不能或者不便的操作,

  • grep下不支持?+这两种meta,但egrep支持;

  • grep 不支持a|b或(abc|xyz)这类“或一”的匹配,但egrep支持;

  • grep 在处理{n,m}时,需要\{ 与 \}处理,但egrep不需。

等诸如此类的。我个人建议能用egrep就不用grep啦...^_^

fgrep: 不作RE处理,表达式仅作一般的字符串处理,所有的meta均市区功能。

好了,关于RE的入门,我们暂时就介绍到这里。 虽然有点乱,且有些观念也不恨精确, 不过,姑且算是对大家的一个交差吧...^_^ 若这两天有时间的话,我在举些范例来分析一下,以帮助大家更好的理解。 假如更有可能的话,也顺道为大家介绍一下sed这个工具。

Part-III eval

讲到command line的重组特性, 真的需要我们好好的加以解释的。

如此便能抽丝剥茧的一层层的将整个command line分析的 一清二楚,而不至于含糊。

假如这个重组的特性理解了,那我们介绍一个好玩的命令:eval.

我们在变量替换的过程中,常会碰到所谓的复式变量的问题: 如:

a=1
A1=abc

我们都知道echo $A1就可以得到abc的结果。 然而,我们能否用$A$a来取代$A1,而同一样替换为abc呢?

这个问题我们可用很轻松的用eval来解决:

evalecho\$A$a

说穿了,eval只不过是在命令行完成替换重组后, 在来一次替换重组罢了... 就是这么简单啦~~~ ^_^

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 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