我正在使用
Get-ChildItem
递归地解析文件夹中的文件以查找某些字符串。
我需要一个正则表达式来从搜索中排除所有形式的注释代码:
/* Excluded */
/********Excluded***********/
//Excluded
/*
排除 */
$My_Regex = "(?s)(?i)(^|\s+?)(\/\*)((.)(?!\*\/))*?(StringsToBeSearched)(.*?)(\*\/)"
$Searched_Results = Get-ChildItem -Recurse $folderpath | Select-String $My_Regex
类似的问题here没有帮助。
搜索需要在不属于任何评论的行上进行。有什么帮助吗?
Powershell版本V5.1。
虽然听起来很简单,但实际上有点棘手。我们可以应用一种称为垃圾桶方法的技术来通过正则表达式来解决这个问题。这个想法是匹配整个匹配中我们不想要的所有东西,并且只匹配稍后提取的组中我们想要的东西,例如
(['"])(?:(?!\1|\\).|\\.)*\1 #discard quoted string first
|\/\/[^\n\r]* # capture single line comments before mutli-line
|\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\/ #c-style multi-line comment
|(StringsToBeSearched)
不幸的是,Search-String 是逐行工作的,并教导它将文件视为单个文件(例如,在这种情况下使用
(?s)
标志似乎会失败。下面的代码除了正确的 multi 中的匹配之外,都可以工作) -行评论。
$rex = '(?s)"(?:(?!"|\\).|\\.)*"|\/\/[^\n\r]*|\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\/|(StringsToBeSearched)'
Get-ChildItem -Recurse "C:\temp" | Select-String $rex | Where-Object { $_.Matches[0].Groups[1].Success }
因此,您基本上被迫首先读取文件内容。当我们这样做时,我们可以让这变得更容易一些,但首先删除我们不想要的所有内容,然后像这样搜索关键字:
$rex = '".*?"|(StringsToBeSearched)'
foreach ($file in (Get-ChildItem -Recurse "C:\temp"))
{
$fileContent = (Get-Content $file.PSPath -Raw) -join ''
$fileContent = $fileContent -replace '(?s)"(?:(?!"|\\).|\\.)*"|\/\/[^\n\r]*|\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\/'
$results = $fileContent | Select-String 'StringsToBeSearched' -AllMatches
if($results.Matches.Success){
Write-Host $file.Name
}
}
如果您不仅需要文件名,还需要行号等,您可以轻松扩展循环内部的逻辑。我希望这有帮助。
(?ms)\/\*.*?\*\/|\/\/.*?(?=$)
,适用于整个文件,优先匹配多行注释,然后是单行注释。(?
)
|更改 Regex 的行为方式 |Microsoft.com|
|m
|多行模式|^
和$
表示每行的开头和结尾(而不是输入字符串的开头和结尾)。|
|s
|单行模式|句号.
匹配每个字符,包括
.|
|\/\*
|匹配:/*
||
|.*?
|匹配任意字符,0次或多次,偷懒|.
=任意字符| *
= 0 或更多 | ?
= 懒惰|
|\*\/
|匹配:*/
||
||
|或运算符|如果左侧的正则表达式无法匹配,则尝试右侧的正则表达式|
|\/\/
|匹配:\\
||
|.*?
|匹配任意字符,0次或多次,偷懒|.
=任意字符| *
= 0 或更多 | ?
= 懒惰|
|(?=$)
|如果在行尾则先行|可能不需要先行,尽量不要同时匹配和替换行尾。|
此代码导致
$Searched_Results
包含所有非注释行,并且 $Searched_ResultsWithoutEmptyLines
包含不为空的非注释行。
这是在以下阶段完成的:
$folderpath = "$PSScriptRoot"
$My_Regex = '(?ms)\/\*.*?\*\/|\/\/.*?(?=$)'
# Read whole files, $RawFiles is an array with each cell containing a file.
$RawFiles = Get-ChildItem -Recurse $folderpath -Filter '*.c' | Get-Content -Raw
# Use the regex to remove comments
$RawFilesNoComments = $RawFiles -replace $My_Regex, ''
# Split all files into lines and save to $Searched_Results
$Searched_Results = $RawFilesNoComments -split '\r?\n'
# Split all files into lines, removing empty lines, and save to $Searched_ResultsWithoutEmptyLines
$Searched_ResultsWithoutEmptyLines = $RawFilesNoComments.Split([char[]]@("`r","`n"), [System.StringSplitOptions]::RemoveEmptyEntries)
这是检索所有未注释行的精简版本:
$Searched_Results = ((Get-ChildItem -Recurse $folderpath -Filter '*.c' | Get-Content -Raw) -replace $My_Regex, '') -split '\r?\n'
这是检索所有未注释且非空的行的精简版本:
$Searched_ResultsWithoutEmptyLines = (Get-ChildItem -Recurse $folderpath -Filter '*.c' | Get-Content -Raw) -replace $My_Regex, '' | & {
process {
$_.Split([char[]]@("`r","`n"), [System.StringSplitOptions]::RemoveEmptyEntries)
}
}