Warm tip: This article is reproduced from serverfault.com, please click

shell-用于替换目录中每个文件中的一行的Powershell Scipt不起作用

(shell - Powershell scipt for replacing a line in every file in directory doesn't work)

发布于 2020-11-30 14:45:25

我想制作一个Powershell脚本,以使更改多个文件中的行变得更快。

这真的是我第一次尝试做这样的事情。我基本上想做的是:

  1. 选择目录中所有扩展名为“ .xml”的文件
  2. 仔细检查每个文件的每一行,并检查其中是否包含以下文本(例如:<package>true</package>)/
  3. 然后,我想将上面的示例文本更改为新文本(示例<package>false</package>:)。此外,如果文件不包含,<package>true</package>我也想添加<package>false</package>
  4. 在那之后,它应该只保存文件而不在其他地方创建一个新文件。

我以前从未真正使用过powershell,所以我做了一些挖掘并想出了这段代码:

$files = Get-ChildItem –Path 'C:\test-XML\XML' -Recurse -Filter *.xml 
$tempFilePath = 'C:\test-XML\newXml'
$find = '<packag>true</package>'
$replace = '<packag>false</package>'


foreach ($f in $files){
   (Get-Content $f) -replace $find, $replace | Add-Content -Path $tempFilePath
}

使用此代码,我应该能够获取目录中的所有文件,然后循环通过该目录并使用更新的文本来更改文本。但这是行不通的,也没有涵盖使任务简化所需的所有要点。

有没有人有见识可以使它工作,甚至可以使其比现在更好。

Questioner
imkeVr
Viewed
0
mklement0 2020-12-01 02:14:38

一般来说,它的价值抽穗灌浆zett42的建议:处理XML文件,它是更强大的使用专用的XML解析器,特别是[xml]System.Xml.XmlDocument)。

如果纯文本处理是一个选项-它听起来就像是给你的-你可以用下面的办法,这将更新所有文件到位; 它针对性能进行了优化:

Get-ChildItem –LiteralPath C:\test-XML\XML -Recurse -Filter *.xml |
  ForEach-Object {
    # CAUTION: This updates files *in place*.
    #          Adjust the encoding as needed.
    Set-Content -Encoding Utf8 $_.FullName -Value (
      (Get-Content -Raw $_.FullName) -replace '<package>true</package>', '<package>false</package>'
    )
  }

笔记:

  • 你的代码的主要问题大概只是一个错字:<packag>-> <package>

  • 文件内容会在适当位置更新,因此为了安全起见,最好先备份文件。如果写回更新内容的过程被中断,则存在文件损坏的假想风险。

  • 为了提高效率,读取整个文件到一个单一的,多行字符串(默认行为是,以输出阵列线,这是很慢),从而只有单个操作是必要的。如果输入字符串中没有匹配项,则按原样返回。Get-Content -Raw-replace

  • 需要注意的是PowerShell中始终使用文件写小命令的默认输出字符编码,而不管编码的输入文件的用途,因此,明确使用Set-Content
    -Encoding参数。

    • 有关Windows PowerShell与PowerShell [Core] v6 +之间的默认编码的更多信息,请参见此答案
  • 请注意,更新后的内容将传递给Set-Content -Value参数,而不是通过管道($content | Set-Content ...);尽管此处的性能几乎没有差别,但假设只传递一个字符串,并进行行数组处理,则它将:如果,例如,$content包含要写入文件的行数组,Set-Content ... -Value $content其速度将比$content | Set-Content ...

  • 为简便起见,Set-ContentandGet-Content调用按位置传递filename参数,该-Path参数隐式绑定到该参数,严格来说,参数接受通配符模式通常,即使你传递文字文件名也可以按预期工作,但是更健壮的方法是使用-LiteralPath参数,如Get-ChildItem上面调用所示;具体来说,包含[]导致-Path参数误解的文字文件名(除非已转义)