PowerShell命令与脚本(12)——文本和正则表达式
2021/6/27 7:16:16
本文主要是介绍PowerShell命令与脚本(12)——文本和正则表达式,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
PowerShell定义文本
使用引号可以定义字符串,如果想让自己定义的字符串原样输出,可以使用单引号。 如果想让自己的定义的字符中的变量被内容替换,表达式被执行可以使用双引号. 1、文本中的特殊字符 如果文本放置在一个闭合的双引号中,Powershell解释器会去寻找特殊字符.在这方便主要有两种特殊字符,一个是变量的前缀“$”,一个是反引号“`”位于数字键1左边。 处理变量 将变量放在字符串中,输出时变量会被替换成变量本身的值或者内容。如果将表达式放置在字符串中,并且使用的格式如“$(expression)”,表达式也会被执行,并被替换成表达式执行的输出。 Powershell转义字符 在其它编程语言中喜欢将反斜杠作为转义字符,但是在Powershell中扮演转义字符角色的不是反斜杠,而是反引号“`”字符串中的反引号,会对紧跟随其后的字符进行特殊处理。例如下面的,在一个字符串中输出双引号,和换行符。 其它的转义字符如下表使用特殊文本命令
1. 字符串操作符
1、格式化字符串 格式化操作符 –F 能够将一个字符串格式化为指定格式,左边是包含通配符的字符串,右边是待插入和替换的字符串。 -F 右边的表达式必选放在圆括号中,作为一个整体,先进行计算,然后在格式化。否则可能会解析错误: 可以在-F的左边放置多个字符串通配符,类似.NET中的String.Format方法。-F右边相应的值或表达式也须要使用逗号分隔。 所有的基本操作符形式都大同小异,要处理的数据位于操作符的左右两边,然后通过操作符建立连接。例如,你可以使用下面的语句将文本中指定的字符串替换成目标文本: -replace操作符有三种实现方式,其它文本操作符也类似地有三种实现方式,像-replace,-ireplace,-creplace,i前缀表示字符串大小写不敏感(insensitive),c前缀表示字符串大小写敏感(case sensitive)。 #下面的例子没有完成替换,因为当前大小写敏感: 第三类i前缀,表示大小写不敏感,和没有前缀的命令逻辑一样(PowerShell中默认的字符串比较是不区分大小写的,所以这里保持一致)。- Index:
- Alignment:
- Format:
[appdomain]::currentdomain.getassemblies() | ForEach-Object { $_.GetExportedTypes() | Where-Object {! $_.IsSubclassof([System.Enum])} } | ForEach-Object { $Methods = $_.getmethods() | Where-Object {$_.name -eq "tostring"} |%{"$_"}; If ($methods -eq "System.String ToString(System.String)") { $_.fullname } }例如,其中的数据类型 ”全局唯一标示符”: System.Guid 因为你会经常使用到它,它是全球通用的,下面会给你一个简单的例子来创建GUID。
扩展:GUID(全局唯一标识符) 全局唯一标识符(GUID,Globally Unique Identifier)是一种由算法生成的二进制长度为128位的数字标识符。GUID主要用于在拥有多个节点、多台计算机的网络或系统中。在理想情况下,任何计算机和计算机集群都不会生成两个相同的GUID。GUID 的总数达到了2^128(3.4×10^38)个,所以随机生成两个相同GUID的可能性非常小,但并不为0。所以,用于生成GUID的算法通常都加入了非随机的参数(如时间),以保证这种重复的情况不会发生。3、固定宽度的制表输出 在一个固定宽度和对齐格式中,显示输出多行文本,要求每一列的输出必选具有固定的宽度。格式化操作符可以设置固定宽度输出。 下面的例子通过DIR返回个目录的中的文件列表,然后通过循环输出,文件名和文件大小,因为文件的名字和大小都是不确定的,长度不一样,所以结果拥挤粗糙,可读性差。 下面固定列宽的结果,就显得可读性强了。要设置列宽可以将一个逗号放置在通配符与列宽编号的中间,负数设置右对齐,正数设置左对齐。
2. PowerShell String对象方法
从之前的章节中,我们知道PowerShell将一切存储在对象中,那这些对象中包含了一系列中的称之为方法的指令。默认文本存储在String对象中,它包含了许多非常有用的处理文本的命令。例如,要确定一个文件的扩展名,可以使用LastIndexOf()获取最后一个字符“.”的位置,继续使用Substring()获取扩展名子串。 另外一条途径,使用Split方法,对文件的完整名称进行分割,得到一个字符串数组,取最后一个元素,PowerShell中可以通过索引-1来获取数组中最后一个元素。 下面的表格会给出String对象的所有方法:3. PowerShell String类方法
使用String类命令: 之前已经讨论过,对象方法和类方法的区别了,再回顾一次。 String对象衍生自string类在控制台输入[String]::然后按Tab键会自动智能提示,这些方法就是String类命令。 Get-Member会返回所有string对象的方法,可以通过参数只返回静态方法,也就是string类命令。使用几率最高的自然Format方法,但是因为PowerShell中已经有了大书特书的-F操作符了,Format方法可以秒杀了。但是Join和Contac还是可以聊聊的。 Join()方法曾经在上一部分演示Split()提到过,它可以将一个数组或者列表字符串合以指定分隔符并成一个字符串。例如自定义一个函数,移除多余的白空格。function RemoveSpace([string]$text){ $Private:array=$text.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries) [string]::Join(" ",$array) }Concat()将多个字符串拼接成一个字符串。 Concat()工作起来类似字符串操作符“+”,类似而已,总有区别。 区别在于第一个左表达式必选是一个String类型,否则,麻烦来了: 此时可以使用: 或者:
简单模式匹配
在验证用户的条目时,模式识别是必要并且常见的任务。例如判断用户的输入的字符串是否是一个合法的网络IP地址,或者电子邮箱。有用并且高效的模式匹配需要一些能代表确切数字和字符的通配符。 许多年前,人们就发明了简单的模式匹配,一直沿用至今。#列出当前目录中的文本文件 dir *.txt # 列出系统目录中以‘n’或‘w’打头的文件 dir $env:windir\[nw]*.* # 列出文件后缀名以‘t’打头,并且后缀名只有三个字符的文件 dir *.t?? # 列出文件中包含一个’e’到’z’之间任意字符的文件 dir *[e-z].*
$ip = Read-Host "IP address" If ($ip -like "*.*.*.*") { "valid" } Else { "invalid" }也可以简单验证电子邮件地址。
$email = ".@." $email -like "*.*@*.*"然而上面的例子也仅能验证一些低级错误,还不是很确切。例如a.b.c.d不是一个有效的IP地址,但是上面的模式匹配却能通过验证。
正则表达式
1. 定义模式
如果你需要更加精确的模式识别需要使用正则表达式。正则表达式提供了更加丰富的通配符。正因为如此,它可以更加详细的描述模式,正则表达式也因此稍显复杂。 使用下面的表格中列出的正则表达式元素,你可以非常精准的描述模式。这些正则表达式元素可以归为三大类。 字符:字符可以代表一个单独的字符,或者一个字符集合构成的字符串。 限定符:允许你在模式中决定字符或者字符串出现的频率。 定位符:允许你决定模式是否是一个独立的单词,或者出现的位置必须在句子的开头还是结尾。 正则表达式代表的模式一般由四种不同类型的字符构成。 文字字符:像”abc”确切地匹配”abc“字符串 转义字符:一些特殊的字符例如反斜杠,中括号,小括号在正则表达式中居于特殊的意义,所以如果要专门识别这些特殊字符需要转义字符反斜杠。就像”\[abc\]”可以识别”[abc]”。 预定义字符:这类字符类似占位符可以识别某一类字符。例如”\d”可以识别0-9的数字。 自定义通配符:包含在中括号中的通配符。例如”[a-d]”识别a,b,c,d之间的任意字符,如果要排除这些字符,可以使用”[^a-d]”。2. 同时搜索不同的词语
有时搜索的词语比较含糊不清,因为这些词语可能有多种写法。你可以使用限定符“?”来标记这些词语作为可选字符。非常简单,把“?”放在可选字符后面即可。这样“?”前的字符就变成了可选字符,而不是非得出现。 注意,此处的字符“?”并不代表任何字符,因为怕你可能会联想到简单模式匹配里面的“?”。正则表达式中的“?”,只是一个限定符,它代表的是指定字符或者子表达式出现的频率。具体到上面的例子,“u?”就确保了字符“u”在模式中不是必需的。常用的其它限定符,还有“*”(出现0次后者多次)和“+”(至少出现一次)。 如果你想标记更多的连续字符作为可选,可以把这些字符放置圆括号中建立子表达式。下面的子表达可以同时识别“Nov”和“November”: 如果你想使用多个可选的搜索词语,可以使用“或”操作符“|”: 如果你想将搜索的词语和固定文本结合在一起,作为可选,仍然可以使用子表达式:3. 大小写敏感
为了和PowerShell的习惯保持一致,操作符-match是大小写不敏感的,如果你想切换至大小写敏感的操作符可以使用“-cmatch” 如果你只想在模式的部分片段中使用大小写敏感,仍旧可以使用-match,但是可以在正则表达式中指定部分模式是不是大小写敏感。跟在“(?i)”结构后的字符大小写不敏感,跟在(?-i)结构后面的字符大小写敏感。下面的例子会演示这个区别,模式中的字符字串 “st”因为被(?-i)标记为大小写敏感,所以可以匹配“TEst”,但不能匹配“TEST”。 但是如果你使用的是.NET framework的对象RegEx,它可以自动的在大小写敏感与不敏感之间切换。既可以使用IgnoreCase参数来显示的指定,也可以在模式中使用上面提到的结构来指定。 [regex]::matches("test", "TEST", "IgnoreCase")4. 文本中搜索信息
正则表达式可以识别模式。它们也可以根据确定的模式从文本中过滤出数据,因此正则表达式是用来处理源文本的一款非常优秀的工具。 例如,你想从一封邮件中过滤出一个确切的电子邮件地址,就可以使用我们之前提到过正则表达式。然后就可以在变量$matches找出返回的结果。在你使用-match操作符时,$matches变量会自动被创建,并存储过滤出的结果。$matches是一个哈希表,你既可以输出一个完整的哈希表,也可以使用在中括号中的名称(键值)逐个访问其中的某个元素。 如果文本中有多个电子邮件,上面的方法还会有效吗?非常遗憾,它不会这样做。操作符-match只会匹配一次正则表达式。因此如果你想在源文本中搜索多个出现的模式,你必须切换至RegEx对象,值得一提的是RegEx对象不像-match,Regex对象默认是大小写敏感的,你要想大小写不敏感,可以参考前面的文章。5. 搜索不同的关键字
你可以使用间隔结构“|”来搜索某一组关键字,然后找出那些实际上出现的关键字: $matches会告诉你那些关键字实际上在字符串上出现了。但是要注意正则表达式中关键字的顺序。是因为第一个匹配到的关键字会被选中决定的。所以接下来的实例,结果感觉不正确: 所以接下来更改关键字的顺序可以让较长的关键字被选中。 或者,你可以更加精确地定制你的正则表达式,记住你实际上搜索的是一个独立的单词。所以在关键字中加入单词边界,让顺序的影响失效。 也确实这样,-match只会搜索第一次匹配,如果你的源文本中可能须要和包含关键字的多次出现,可以重新使用RegEx 上面的例子使用了.NET中的原始正则表达式类。6. 组
一串原始的文本行通常有大量有用信息,你可以使用子表达式来收集数据,可以在之后单独使用。基本的规则是所有想通过模式来搜索的数据应当放在圆括号中,因为变量$matches会将这些子表达式以单独的序列返回。如果文本行首先包含了数据,然后是其它文本,两者之间以制表符分割,你可以如下描述这段模式: 当使用子表达式时,$matches会包含所有搜索模式,数组的第一个元素命名为“0”,子表达式分别位于两个圆括号中,为了使他们更加便于读取理解,你可以分配给每个子表达式它们自己的名子(键),接下来通过它们去调用匹配的结果。给子表达式命名,可以在圆括号中输入type ?。 每个子表达式检索的结果都需要存储空间,如果特定场合中不需要这些结果可以,可以丢弃它们,因为这样可以提高正则表达式匹配的速度。要丢弃结果,可以在子表达式中的第一个语句上加上“?:”7. 深入使用子表达式
借住子表达式的帮助,你可以创建出更加惊人和灵活的正则表达式。例如,怎样定义一个网站中HTML标签的模式呢?一个标签通常包含同样的结构:<tagname [parameter]>…</tagname>,这就意味着可以快速定义出一个非常严格的HTML标签模式: 模式以固定的文本”<body “开始,额外的字符以单词为界。接下来跟着右括号”>”,”>”之后则是中的内容,这些内容可以由任意数量的字符(.*?)组成。圆括号中是一个子表达式,会在$matches中返回检索到的中的结果。结尾的部分为固定文本 “”)开始,另外一次以(“”)终结。如果一个正则表达式支持处理任意标签,那它必须能够自动地找出所有的标签,并且在前后两个位置都能使用。怎样完成它呢?像这样: 上面的正则表达式不在包含预定义的固定HTML 标签,却能匹配所有的HTML标签。它是如何办到的呢?因为初始标签被定义成子表达式,该子表达式以字母开始,可以由任意字母或数字组成。 ([A-Z][A-Z0-9]*) 在开始匹配到的标签必须在之后也能迭代匹配到,就是要有头也得有尾,善始善终。此处你会发现引入了一个新写法””,“\1”引用的是第一个子表达式。这样就保证了HTML标签开始的和结尾的一致了。8. 贪婪与非贪婪匹配
根据正则表达式的规则,读者可能会怀疑在匹配HTML标签时,使用的事“.*?”而不是简单的“.*”。毕竟“.*”已经可以匹配足够的字符了。“.*”和“.*?”之间的不同并不容易识别。下面通过一个例子来澄清。 假设你要再一个长文件中匹配英文月份,但是月份并不是以同样的方式出现的。有时使用短格式,有时使用长格式。正如接下来看见的一样,正则表达式完成可以做到。因为正则表达式支持子表达式以可选的形式出现。 上面两种情况正则表达式都能识别月份,但是返回的结果却不相同,一个是Feb,一个是February。默认,正则表达式属于“贪婪”模式。在搜索到Feb后会继续贪婪地搜索更多符合模式的的字符。如果可以整个文本会返回。 然后,如果你主要关心的只是规范的月份名称,你可能更喜欢获取缩写的月份名称。这也正是“??”限定符做的,它会将正则表达式转换成“非贪婪”模式,一旦他识别到一个模式,就会立即返回,不再会检查可选的子表达式是否匹配。 到底限定符“??”和之前的例子中的限定符“*?”有什么联系呢?事实上“*?”不是一个独立量词。它会将“贪婪”模式转换成“非贪婪”模式。这就意味着,你可以使用“?”强制将限定符“*”转换成非贪婪模式,尽可能返回短结果。9. 搜索字符片段
举一个例子演示怎样通过正则表达式轻松的搜索字符串片段。下面会匹配位于两个特定单词中的字符串,并且字符的长度介于1到6之间。10. 替换字符串
之前介绍过-replace操作符,你可以能已经知道了怎样替换字符串中的字串。让我们来回顾一下: 但是这种简单的替换不可能永远都是高效的,因此可以尝试使用正则表达式来完成替换工作。下面有一个好玩的例子,用来演示它怎样实用。 也许你会碰到将多个类似的词语替换成同一个词语这样的需求。如果没有正则表达式,需要重复使用replace操作符多次。而每一次replace都会伴随一次遍历,效率明显很低。取而代之,如果使用正则表达式,则非常方便。 你可以在括号中输入任意的词语,多个词语之间用“|”隔开,这样所有的词语都会被指定的字符串替换掉。11. 使用反向引用
最后一个例子在一个字符串中替换了多个指定的关键字。通常效率还是挺高的,但是有时候你可能不想替换所有出现的关键字,而只是想替换出现在特殊上下文中的关键字。这样的情况下,上下文必须定义在模式中。例如,怎样更改正则表达式,让它只替换名字Miller和Meyer. 输出结果看起来有点奇怪,但是确实是和搜索模式匹配的。被替换掉的仅仅是Mr.或者Mrs. Miller和Mr. 或者 Mrs. Meyer。词语”Mr. Werner”没有被替换。遗憾的是结果没道理替换掉整个模式,至少人名应当保留。这可能吗? 此时反向引用应当登场了。在正则表达式中,不论你什么时候使用圆括号,圆括号中的结果都是分开被评估的。你可以在你的“替换串”中使用这些分离出来的结果。第一个子表达式的结果总是”Mr.” 或者 “Mrs.”。第二个子表达式总是返回人名。词语”$1” 和 “$2″在“替换串”中提供了你的子表达式(因此,数字是一串连续的数字;对于补充的子表达式你可以使用”$3″)。 奇怪的是,第一个反向引用似乎并没有工作。当然原因也非常明显: “$1” and “$2″看起来是PowerShell 变量, 但是实际上它们应当是操作符-replace的正则表达式词语。导致此结果的是你把“替换串”放在了双引号中了,PowerShell会将变量替换成具体的值,而这个值一般情况下应当为空。所以要是反向引用在“替换串”中起作用,你必须将“替换串”放置在单引号中,这样让$变成普通字符,这样PowerShell就不会把它识别为自己的变量了,并完成替换功能:12. 在文本中插入字符
“替换串”可以由多行文本中的多个实例组成。例如,在你平时回复一封邮件时,你可能在新邮件中会通过在行首添加 “>” 符号来引用原邮件的中的内容。正则表达式就可以做这样的标记。 然而,要完成它,你可能得稍微了解一点“多行”模式。通常,该模式是关闭的,此时限定符”^”代表文本的开始,”$”代表文本的结束。要让这两个限定符可以代表文本行的开始和文本行的结束,必须使用”(?m)”来开启“多行”模式。只有这样,–replace 才会在每个单独的文本行之间替换模式。在“多行”模式开启后,限定符”^” 和 “\A”,还有”$” and “\Z”会顿时拥有不同的表现。”\A”仍然会标志文本的开始,而”^”则会标志文本行的开始。”\Z”仍然会标志文本的结尾,而”$”则会标志文本行结尾。13. 删除多余的空格
使用正则表达式可以完成一些日常任务,比如一处一个字符串中多余的白空格。模式需要描述一个空格(字符:“\s”)至少出现两次(限定符:“{2,}”)。然后以一个正常的单空格字符替换。 "太多 太多的 空格 怎么才能减少 " -replace "\s{2,}" ," "14. 搜索和移除重复的单词
怎样才能移除文本中多余的单词。这里,仍旧可以再次使用空格。模式可以这样定义: "\b(\w+)(\s+\1){1,}\b" 模式会搜索一个单词(以“\b”定位),它由一个单词组成(字符“\w” 和限定符“+”),白空格紧随以后(字符“\s”和限定符“?”)。该模式中,白空格字符和将要被替换的单词必须至少出现一次(至少一次或者更多次,使用限定符“{1,}”)。整个模式会被第一次出现的反向引用给替换掉,也就是位于第一个的单词。15. 非捕获组
(expression) 是一种简单的子表达式 (?:expression)是一种特殊的子表达式 后者不会将子表达式的匹配结果加入组中文本处理实例
(一) 问题描述: 有如下一段文本文件,开头有许多描述,字符“~”为有用数据的开始标志,要求:求所有电阻值的个数,平均值,总和,最大值,最小值。#加载文件,并过滤空行 $fullText=Get-Content .\a.txt | where { !([string]::IsNullOrWhiteSpace($_))} #寻找文件头开始标志 $startFlagIndex=-1 For ($i = 0; $i -lt $fullText.Length; $i++) { if($fullText[$i].Contains("~")) { $startFlagIndex=$i break } } #去掉文件头 $fullText=$fullText | Select-Object -Skip ($startFlagIndex+1) <# #将文件转换成CSV格式,然后再从CSV转换成对象 #几经周折后,再要深入进行数据处理,将会变得非常方便 #> $objs=$fullText | foreach{ $tokens= $_.Split(' ',[StringSplitOptions]::RemoveEmptyEntries) '"{0}"' -f [string]::Join('","',$tokens) } | ConvertFrom-Csv #统计放射性值不为空的对象 Write-Host "统计放射性值为空的对象" $objs | where { $_.放射性值 -ne $null } | Format-Table -AutoSize #求所有电阻值的个数,平均值,总和,最大值,最小值 Write-Host "求所有电阻值的个数,平均值,总和,最大值,最小值:" $objs | Measure-Object -Property 电阻值 -Average -Sum -Maximum -Minimum输出示例: 回过头再看,脚本完全可以优化为一个foreach循环,每行文本只遍历一次。之所以多次一举,是为了演示分析问题的过程。 同样也能得出一个结论,如果可以尽最大可能从数据源拿到CSV文件格式的数据,PowerShell处理起来更方便,一行搞定! (二) 问题描述: 给出一段学生成绩文本文件如下:
$scoreTables=@{} $stus=Get-Content .\ScoresFile.txt | foreach { $stu=$_ -split " " if($scoreTables.ContainsKey($stu[1])) { $scoreTables[$stu[1]]++ } else { $scoreTables[$stu[1]]=1 } @{ Score=$stu[1];Name=$stu[0] } } $stus | where { $scoreTables[$_.Score] -gt 1 } | foreach {"{0} {1}" -f $_.Name,$_.Score }用group-object实现统计,比较完美,稍作整理,也贴在这里:
Get-Content .\ScoresFile.txt | ForEach-Object { [PSCustomObject]@{ Name = $_.split()[0] Value = $_.split()[1] } } | Group-Object Value | Where-Object { $_.Count -gt 1 }| ForEach-Object { $_.Group | ForEach-Object { "{0} {1}" -f $_.name,$_.value } } #为了和源文件格式保持一直,加入格式化(三) 原始文本:”data1″:111,”data2″:22,”data3″:3,”data4″:4444444,”data5″:589 要求:转换成对象
PowerShell命令与脚本(8)——循环
输出: (四)提取CSV文件中的域名扩展:CSV 逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。纯文本意味着该文件是一个字符序列,不含必须像二进制数字那样被解读的数据。CSV文件由任意数目的记录组成,记录间以某种换行符分隔;每条记录由字段组成,字段间的分隔符是其它字符或字符串,最常见的是逗号或制表符。通常,所有记录都有完全相同的字段序列。 “CSV”并不是一种单一的、定义明确的格式(尽管RFC 4180有一个被通常使用的定义)。因此在实践中,术语“CSV”泛指具有以下特征的任何文件:有一个CSV文件,其中包含了成千上万的URL链接,每个链接都可能是完整路径包含了文件夹,变量等。希望提取出其中的域名以便于进行深度分析。 我的CSV文件只有一列:在这些常规的约束条件下,存在着许多CSV变体,故CSV文件并不完全互通。然而,这些变异非常小,并且有许多应用程序允许用户预览文件(这是可行的,因为它是纯文本),然后指定分隔符、转义规则等。如果一个特定CSV文件的变异过大,超出了特定接收程序的支持范围,那么可行的做法往往是人工检查并编辑文件,或通过简单的程序来修复问题。因此在实践中,CSV文件还是非常方便的。
- 纯文本,使用某个字符集,比如ASCII、Unicode、EBCDIC或GB2312(简体中文环境)等;
- 由记录组成(典型的是每行一条记录);
- 每条记录被分隔符分隔为字段(典型分隔符有逗号、分号或制表符;有时分隔符可以包括可选的空格);
- 每条记录都有同样的字段序列。
这篇关于PowerShell命令与脚本(12)——文本和正则表达式的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-11正则表达式教程:初学者指南
- 2024-09-11正则表达式学习:入门指南与实践技巧
- 2024-08-15正则表达式入门:基础教程与实践指南
- 2024-01-0939. 干货系列从零用Rust编写负载均衡及代理,正则及格式替换
- 2024-01-08如何编写高效的正则表达式?
- 2023-12-29"Matlab中的正则表达式:强大而灵活的工具"
- 2023-09-30这个正则 为啥同样的单号第二个就提取不出来?
- 2023-06-086.2 re 正则表达式
- 2023-06-06将字符串里的\x01,\x02这些替换掉用正则表达式无效?
- 2023-05-24正则表达式详解