首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Powershell regex替换文件中的urls

Powershell regex替换文件中的urls
EN

Stack Overflow用户
提问于 2019-11-19 13:49:29
回答 2查看 664关注 0票数 2

我正在尝试匹配regex并在文件中替换匹配。我的正则表达式如下(与之匹配很好):

代码语言:javascript
复制
$regex1 = [regex] "index.php\?title\=[a-zA-Z0-9_]*"

我试图在以下文件中运行替换的源文件的经过编辑的摘录:

代码语言:javascript
复制
<content:encoded>
    <![CDATA[<a href="http://[redacted]/index.php?title=User_Manual">
    <a href="http://[redacted]/index.php?title=User_Manual">The software</a>, running on the 
    <a href="http://[redacted]/index.php?title=Mobile_Device">POS Device</a>, enables 
    <a href="http://[redacted]/index.php?title=Logging_In">log in</a>, 
    <a href="http://[redacted]/index.php?title=Selecting_Journey">select a journey</a>

而Powershell替换:

代码语言:javascript
复制
.Replace("index.php?title=","").Replace("_","-").ToLower())

我提取了所有匹配项,将$allmatches数组强制转换为一个新数组(因此它是可写的),然后更新新数组中的值。我想不出如何将它写回文件,而且似乎找不到任何文章或文档来帮助解决这个问题。我的代码到目前为止:

代码语言:javascript
复制
$regex1 = [regex] "index.php\?title\=[a-zA-Z0-9_]*"

$contentOf=Get-Content $contentfile
$allmatches=$regex1.Matches($contentOf)
$totalcount=$allmatches.Count

$newArray = $allmatches | select *

for($i=0;$i -le $totalCount;$i++) {
    $newvalue=(($allmatches[$i].Value).Replace("index.php?title=","").Replace("_","-").ToLower())
    $newArray[$i].Value = $newvalue
}

此时,我有一个包含所有regex匹配和替换的数组$newArray,但不知道如何将其写回我的文件/变量(如$newarray[0] )。

代码语言:javascript
复制
Groups   : {0}
Success  : True
Name     : 0
Captures : {0}
Index    : 4931
Length   : 40
Value    : user-manual

当然,我这样做完全是错误的。就我为什么选择Powershell来做这件事而言,仅仅是因为这是我花了大部分时间编写这些days...of课程的地方,我确信它在shell中是可以实现的(我需要更长的时间才能达到这个目的)。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-11-19 17:16:28

实际上,这是在regex和正则表达式中的.Net替换中使用捕获组的好地方。修改后的正则表达式是:

代码语言:javascript
复制
$regex = [regex] 'index\.php\?title\=(\p{L}*)_(\p{L}*)'
  • \p{L}匹配任何字母(由Unicode定义,而不仅仅是A)。
  • (\p{L}*)是一个只包含字母的编号捕获组。
  • 替换模式字符串将使用$1$2来引用每个捕获组:'$1-$2'。注意在替换字符串上使用单引号'',以防止PowerShell变量在$1$2上展开。

简单替代

如果我们只关心捕获组,我们可以只使用以下代码:

代码语言:javascript
复制
    $testContent = @'
<content:encoded>
    <![CDATA[<a href="http://[redacted]/index.php?title=User_Manual">
    <a href="http://[redacted]/index.php?title=User_Manual">The software</a>, running on the
    <a href="http://[redacted]/index.php?title=Mobile_Device">POS Device</a>, enables
    <a href="http://[redacted]/index.php?title=Logging_In">log in</a>, 
    <a href="http://[redacted]/index.php?title=Selecting_Journey">select a journey</a>
    '@
    $regex = [regex] 'index\.php\?title\=(\p{L}*)_(\p{L}*)'
    $modifiedContent = [regex]::Replace($testContent, $regex, '$1-$2')

其结果是:

代码语言:javascript
复制
<content:encoded>
<![CDATA[<a href="http://[redacted]/index.php?title=User_Manual">
<a href="http://[redacted]/index.php?title=User_Manual">The software</a>, running on the
<a href="http://[redacted]/index.php?title=Mobile_Device">POS Device</a>, enables
<a href="http://[redacted]/index.php?title=Logging_In">log in</a>, 
<a href="http://[redacted]/index.php?title=Selecting_Journey">select a journey</a>

这种方法的问题是,不允许我们将组更改为小写。正则表达式实际上并没有处理这个需求的方法。幸运的是,.Net有一个扩展,允许我们轻松地处理更复杂的情况。

使用MatchEvaluator委托

MatchEvaluator是一个对象,可以与regex替换方法的重载一起使用,用于正常替换不足的情况。在PowerShell中,它们可以是一个简单的具有[Match]参数的scriptblock:

代码语言:javascript
复制
    $testContent = @'
    <content:encoded><![CDATA[<a href="http://[redacted]/index.php?title=User_Manual">
   <content:encoded>
    <![CDATA[<a href="http://[redacted]/index.php?title=User_Manual">
    <a href="http://[redacted]/index.php?title=User_Manual">The software</a>, running on the
    <a href="http://[redacted]/index.php?title=Mobile_Device">POS Device</a>, enables
    <a href="http://[redacted]/index.php?title=Logging_In">log in</a>, 
    <a href="http://[redacted]/index.php?title=Selecting_Journey">select a journey</a>
    '@
    $regex = [regex] 'index\.php\?title\=(\p{L}*)_(\p{L}*)'
    $MatchEvaluator = {
        param($match)    
        $group1 = $match.Groups[1].Value.toLower()
        $group2 = $match.Groups[2].Value.toLower()
        return "$group1-$group2"
    }
    [regex]::Replace($testContent, $regex, $MatchEvaluator)

它提供了所期望的结果:

代码语言:javascript
复制
<content:encoded>
    <![CDATA[<a href="http://[redacted]/index.php?title=User_Manual">
    <a href="http://[redacted]/index.php?title=User_Manual">The software</a>, running on the
    <a href="http://[redacted]/index.php?title=Mobile_Device">POS Device</a>, enables
    <a href="http://[redacted]/index.php?title=Logging_In">log in</a>, 
    <a href="http://[redacted]/index.php?title=Selecting_Journey">select a journey</a>

替换文件的内容

最后的代码如下所示:

代码语言:javascript
复制
# Load the file as a single string
$content = Get-Content $contentfile -Raw

# Regex to replace, with capturing groups
$regex = [regex] 'index\.php\?title\=(\p{L}*)_(\p{L}*)'

# Delegate to transfrom capture groups into lowercase
$MatchEvaluator = {
    param($match)
    $group1 = $match.Groups[1].Value.toLower()
    $group2 = $match.Groups[2].Value.toLower()
    return "$group1-$group2"
}

# Replace all matches of the regular expression with delegate
$modifiedContent = [regex]::Replace($Content, $regex, $MatchEvaluator)

# Overwrite existing file
$modifiedContent | Out-File $contentfile
票数 3
EN

Stack Overflow用户

发布于 2019-11-19 14:17:00

我提取了所有匹配项,将$allmatches数组强制转换为一个新数组(因此它是可写的),然后更新新数组中的值。

你不需要这样做,问题的解决要简单得多。您所需要做的就是对原始文件使用Get-Content,并对每一行进行迭代。您还可以使用-replace运算符而不是[Regex]类来处理替换:

代码语言:javascript
复制
Get-Content $contentFile | Foreach-Object {
  $_ = ( $_ -replace 'index.php\?title=' ) -replace '_', '-'
} | Set-Content $contentFile

您可以直接将Get-Content的结果输送到Foreach-Object。对于每一行,我们希望用一个空字符串替换index.php\?title= (您可以省略-replace的第二个参数作为这个参数的缩写)。然后,我们还将该行的_替换为-。它对文件中的每一行执行此操作。然后将更改的内容以管道方式发送到Set-Content,然后将其写回原始文件。

作为旁白,当您使用-match运算符(我们在上面没有使用它)匹配正则表达式时,您可以检查自动$Matches变量,以了解表达式如何与字符串匹配,该字符串类似于[Regex]::Matches返回的内容。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58935570

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档