337

使用 PowerShell,我想[MYID]MyValue. 最简单的方法是什么?

4

14 回答 14

506

使用(V3版本):

(Get-Content c:\temp\test.txt).replace('[MYID]', 'MyValue') | Set-Content c:\temp\test.txt

或者对于 V2:

(Get-Content c:\temp\test.txt) -replace '\[MYID\]', 'MyValue' | Set-Content c:\temp\test.txt
于 2013-06-17T09:35:15.167 回答
93

我更喜欢使用 .NET 的文件类及其静态方法,如下例所示。

$content = [System.IO.File]::ReadAllText("c:\bla.txt").Replace("[MYID]","MyValue")
[System.IO.File]::WriteAllText("c:\bla.txt", $content)

这具有使用单个 String 而不是使用 String-array 的优点Get-Content。这些方法还负责文件的编码(UTF-8 BOM等),而您大部分时间都不必注意。

此外,与使用 Get-Content 并通过管道传递到Set-Content的算法相比,这些方法不会弄乱行尾(可能使用的 Unix 行尾)。

所以对我来说:多年来可能破坏的东西越来越少。

使用 .NET 类时鲜为人知的一点是,当您在 PowerShell 窗口中键入“[System.IO.File]::”时,您可以按Tab键逐步执行那里的方法。

于 2015-06-17T14:05:25.673 回答
90
(Get-Content file.txt) | 
Foreach-Object {$_ -replace '\[MYID\]','MyValue'}  | 
Out-File file.txt

注意周围的括号(Get-Content file.txt)是必需的:

如果没有括号,内容将被读取,一次一行,并沿管道向下流动,直到它到达 out-file 或 set-content,它试图写入同一个文件,但它已经被 get-content 打开,你得到一个错误。括号使内容读取的操作执行一次(打开、读取和关闭)。只有当所有行都被读取后,它们才会一次通过管道传输,当它们到达管道中的最后一个命令时,它们才能写入文件。与 $content=content; 相同 $内容 | 在哪里 ...

于 2013-06-17T10:55:09.157 回答
27

上面的仅对“一个文件”运行,但您也可以对文件夹中的多个文件运行它:

Get-ChildItem 'C:yourfile*.xml' -Recurse | ForEach {
     (Get-Content $_ | ForEach  { $_ -replace '[MYID]', 'MyValue' }) |
     Set-Content $_
}
于 2016-07-20T20:04:14.310 回答
10

你可以尝试这样的事情:

$path = "C:\testFile.txt"
$word = "searchword"
$replacement = "ReplacementText"
$text = get-content $path 
$newText = $text -replace $word,$replacement
$newText > $path
于 2013-06-17T09:43:44.130 回答
8

这是我使用的,但在大型文本文件上速度很慢。

get-content $pathToFile | % { $_ -replace $stringToReplace, $replaceWith } | set-content $pathToFile

如果您要替换大型文本文件中的字符串并且速度是一个问题,请考虑使用System.IO.StreamReaderSystem.IO.StreamWriter

try
{
   $reader = [System.IO.StreamReader] $pathToFile
   $data = $reader.ReadToEnd()
   $reader.close()
}
finally
{
   if ($reader -ne $null)
   {
       $reader.dispose()
   }
}

$data = $data -replace $stringToReplace, $replaceWith

try
{
   $writer = [System.IO.StreamWriter] $pathToFile
   $writer.write($data)
   $writer.close()
}
finally
{
   if ($writer -ne $null)
   {
       $writer.dispose()
   }
}

(上面的代码没有经过测试。)

使用 StreamReader 和 StreamWriter 替换文档中的文本可能有一种更优雅的方法,但这应该会给您一个很好的起点。

于 2014-08-13T06:51:10.577 回答
8

我从 Payette 的Windows Powershell in Action 中找到了一种鲜为人知但非常酷的方法。您可以引用变量之类的文件,类似于 $env:path,但您需要添加花括号。

${c:file.txt} = ${c:file.txt} -replace 'oldvalue','newvalue'
于 2019-06-07T18:48:52.277 回答
2

归功于@rominator007

我把它包装成一个函数(因为你可能想再次使用它)

function Replace-AllStringsInFile($SearchString,$ReplaceString,$FullPathToFile)
{
    $content = [System.IO.File]::ReadAllText("$FullPathToFile").Replace("$SearchString","$ReplaceString")
    [System.IO.File]::WriteAllText("$FullPathToFile", $content)
}

注意:这不区分大小写!!!!

看到这篇文章:String.Replace 忽略大小写

于 2018-09-01T03:31:46.880 回答
2

如果您需要替换多个文件中的字符串:

应该注意的是,此处发布的不同方法在完成时间方面可能大不相同。对我来说,我经常有大量的小文件。为了测试什么是最高效的,我在 40,693 个单独的文件中提取了 5.52 GB(5,933,604,999 字节)的 XML,并查看了我在这里找到的三个答案:

## 5.52 GB (5,933,604,999 bytes) of XML files (40,693 files) 
$xmls = (Get-ChildItem -Path "I:\TestseT\All_XML" -Recurse -Filter *.xml).FullName

#### Test 1 - Plain Replace
$start = Get-Date
foreach ($xml in $xmls) {
    (Get-Content $xml).replace("'", " ") | Set-Content $xml
}
$end = Get-Date
New-TimeSpan –Start $Start –End $End
# TotalMinutes: 103.725113128333

#### Test 2 - Replace with -Raw
$start = Get-Date
foreach ($xml in $xmls) {
    (Get-Content $xml -Raw).replace("'", " ") | Set-Content $xml
}
$end = Get-Date
New-TimeSpan –Start $Start –End $End
# TotalMinutes: 10.1600227983333

#### Test 3 - .NET, System.IO
$start = Get-Date
foreach ($xml in $xmls) {
    $txt = [System.IO.File]::ReadAllText("$xml").Replace("'"," ") 
    [System.IO.File]::WriteAllText("$xml", $txt)
}
$end = Get-Date
New-TimeSpan –Start $Start –End $End
# TotalMinutes: 5.83619516833333
于 2020-01-13T19:52:41.227 回答
1

由于这经常出现,我为它定义了一个函数。我默认为区分大小写、基于正则表达式的匹配,但我包含了用于定位文字文本和忽略大小写的开关。

# Find and replace text in each pipeline string.  Omit the -Replace parameter to delete
# text instead.  Use the -SimpleMatch switch to work with literal text instead of regular
# expressions.  Comparisons are case-sensitive unless the -IgnoreCase switch is used.
Filter Edit-String {
    Param([string]$Find, [string]$Replace='', [switch]$SimpleMatch, [switch]$IgnoreCase) 

    if ($SimpleMatch) {
        if ($IgnoreCase) {
            return $_.Replace($Find, $Replace,
                [System.StringComparison]::OrdinalIgnoreCase)
        }
        return $_.Replace($Find, $Replace)
    }
    if ($IgnoreCase) {
        return $_ -replace $Find, $Replace
    }
    return $_ -creplace $Find, $Replace
}

Set-Alias replace Edit-String
Set-Alias sc Set-Content  

用法

# 1 file
$f = a.txt; gc $f | replace '[MYID]' 'MyValue' -SimpleMatch | sc $f

# 0 to many files
gci *.txt | % { gc $_ | replace '\[MYID\]' 'MyValue' | sc $_ }

# Several replacements chained together
... | replace '[1-9]' T | replace a b -IgnoreCase | replace 'delete me' | ...

# Alias cheat sheet
#  gci Get-ChildItem
#  gc  Get-Content
#  sc  Set-Conent
#  %   ForEach-Object
于 2021-10-01T04:27:23.997 回答
0

这对我使用 PowerShell 中的当前工作目录有用。您需要使用该FullName属性,否则它在 PowerShell 版本 5 中不起作用。我需要更改所有CSPROJ文件中的目标 .NET 框架版本。

gci -Recurse -Filter *.csproj |
% { (get-content "$($_.FullName)")
.Replace('<TargetFramework>net47</TargetFramework>', '<TargetFramework>net462</TargetFramework>') |
 Set-Content "$($_.FullName)"}
于 2017-05-08T18:55:15.300 回答
0

有点旧和不同,因为我需要在特定文件名的所有实例中更改某一行。

此外,Set-Content没有返回一致的结果,所以我不得不求助于Out-File.

下面的代码:


$FileName =''
$OldLine = ''
$NewLine = ''
$Drives = Get-PSDrive -PSProvider FileSystem
foreach ($Drive in $Drives) {
    Push-Location $Drive.Root
        Get-ChildItem -Filter "$FileName" -Recurse | ForEach { 
            (Get-Content $_.FullName).Replace($OldLine, $NewLine) | Out-File $_.FullName
        }
    Pop-Location
}

这是在这个 PowerShell 版本上最适合我的方法:

Major.Minor.Build.Revision

5.1.16299.98

于 2017-12-19T15:05:54.733 回答
0

这是一个相当简单的支持多行正则表达式,多个文件(使用管道),指定输出编码等。由于该ReadAllText方法,不推荐用于非常大的文件。

# Update-FileText.ps1

#requires -version 2

<#
.SYNOPSIS
Updates text in files using a regular expression.

.DESCRIPTION
Updates text in files using a regular expression.

.PARAMETER Pattern
Specifies the regular expression pattern.

.PARAMETER Replacement
Specifies the regular expression replacement pattern.

.PARAMETER Path
Specifies the path to one or more files. Wildcards are not supported. Each file is read entirely into memory to support multi-line searching and replacing, so performance may be slow for large files.

.PARAMETER CaseSensitive
Specifies case-sensitive matching. The default is to ignore case.

.PARAMETER SimpleMatch
Specifies a simple match rather than a regular expression match (i.e., the Pattern parameter specifies a simple string rather than a regular expression).

.PARAMETER Multiline
Changes the meaning of ^ and $ so they match at the beginning and end, respectively, of any line, and not just the beginning and end of the entire file. The default is that ^ and $, respectively, match the beginning and end of the entire file.

.PARAMETER UnixText
Causes $ to match only linefeed (\n) characters. By default, $ matches carriage return+linefeed (\r\n). (Windows-based text files usually use \r\n as line terminators, while Unix-based text files usually use only \n.)

.PARAMETER Overwrite
Overwrites a file by creating a temporary file containing all replacements and then replacing the original file with the temporary file. The default is to output but not overwrite.

.PARAMETER Force
Allows overwriting of read-only files. Note that this parameter cannot override security restrictions.

.PARAMETER Encoding
Specifies the encoding for the file when -Overwrite is used. Possible values for this parameter are ASCII, BigEndianUnicode, Unicode, UTF32, UTF7, and UTF8. The default value is ASCII.

.INPUTS
System.IO.FileInfo.

.OUTPUTS
System.String (single-line file) or System.String[] (file with more than one line) without the -Overwrite parameter, or nothing with the -Overwrite parameter.

.LINK
about_Regular_Expressions

.EXAMPLE
C:\> Update-FileText.ps1 '(Ferb) and (Phineas)' '$2 and $1' Story.txt

This command replaces the text 'Ferb and Phineas' with the text 'Phineas and Ferb' in the file Story.txt and outputs the content. Note that the pattern and replacement strings are enclosed in single quotes to prevent variable expansion.

.EXAMPLE
C:\> Update-FileText.ps1 'Perry' 'Agent P' Story2.txt -Overwrite

This command replaces the text 'Perry' with the text 'Agent P' in the file Story2.txt.
#>

[CmdletBinding(SupportsShouldProcess = $true,ConfirmImpact = "High")]
param(
  [Parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true)]
  [String[]] $Path,

  [Parameter(Mandatory = $true,Position = 1)]
  [String] $Pattern,

  [Parameter(Mandatory = $true,Position = 2)]
  [AllowEmptyString()]
  [String] $Replacement,

  [Switch] $CaseSensitive,

  [Switch] $SimpleMatch,

  [Switch] $Multiline,

  [Switch] $UnixText,

  [Switch] $Overwrite,

  [Switch] $Force,

  [ValidateSet("ASCII","BigEndianUnicode","Unicode","UTF32","UTF7","UTF8")]
  [String] $Encoding = "ASCII"
)

begin {
  function Get-TempName {
    param(
      $path
    )
    do {
      $tempName = Join-Path $path ([IO.Path]::GetRandomFilename())
    }
    while ( Test-Path $tempName )
    $tempName
  }

  if ( $SimpleMatch ) {
      $Pattern = [Regex]::Escape($Pattern)
  }
  else {
    if ( -not $UnixText ) {
      $Pattern = $Pattern -replace '(?<!\\)\$','\r$'
    }
  }

  function New-Regex {
    $regexOpts = [Text.RegularExpressions.RegexOptions]::None
    if ( -not $CaseSensitive ) {
      $regexOpts = $regexOpts -bor [Text.RegularExpressions.RegexOptions]::IgnoreCase
    }
    if ( $Multiline ) {
      $regexOpts = $regexOpts -bor [Text.RegularExpressions.RegexOptions]::Multiline
    }
    New-Object Text.RegularExpressions.Regex $Pattern,$regexOpts
  }

  $Regex = New-Regex

  function Update-FileText {
    param(
      $path
    )
    $pathInfo = Resolve-Path -LiteralPath $path
    if ( $pathInfo ) {
      if ( (Get-Item $pathInfo).GetType().FullName -eq "System.IO.FileInfo" ) {
        $fullName = $pathInfo.Path
        Write-Verbose "Reading '$fullName'"
        $text = [IO.File]::ReadAllText($fullName)
        Write-Verbose "Finished reading '$fullName'"
        if ( -not $Overwrite ) {
          $regex.Replace($text,$Replacement)
        }
        else {
          $tempName = Get-TempName (Split-Path $fullName -Parent)
          Set-Content $tempName $null -Confirm:$false
          if ( $? ) {
            Write-Verbose "Created file '$tempName'"
            try {
              Write-Verbose "Started writing '$tempName'"
              [IO.File]::WriteAllText("$tempName",$Regex.Replace($text,$Replacement),[Text.Encoding]::$Encoding)
              Write-Verbose "Finished writing '$tempName'"
              Write-Verbose "Started copying '$tempName' to '$fullName'"
              Copy-Item $tempName $fullName -Force:$Force -ErrorAction Continue
              if ( $? ) {
                Write-Verbose "Finished copying '$tempName' to '$fullName'"
              }
              Remove-Item $tempName
              if ( $? ) {
                Write-Verbose "Removed file '$tempName'"
              }
            }
            catch [Management.Automation.MethodInvocationException] {
              Write-Error $Error[0]
            }
          }
        }
      }
      else {
        Write-Error "The item '$path' must be a file in the file system." -Category InvalidType
      }
    }
  }
}

process {
  foreach ( $PathItem in $Path ) {
    if ( $Overwrite ) {
      if ( $PSCmdlet.ShouldProcess("'$PathItem'","Overwrite file") ) {
        Update-FileText $PathItem
      }
    }
    else {
      Update-FileText $PathItem
    }
  }
}

也可作为Github 上的要点

于 2020-10-09T22:16:33.173 回答
-1

Set-Content 命令的小修正。如果未找到搜索到的字符串,该Set-Content命令将空白(清空)目标文件。

您可以先验证您要查找的字符串是否存在。如果不是,它不会取代任何东西。

If (select-string -path "c:\Windows\System32\drivers\etc\hosts" -pattern "String to look for") `
    {(Get-Content c:\Windows\System32\drivers\etc\hosts).replace('String to look for', 'String to replace with') | Set-Content c:\Windows\System32\drivers\etc\hosts}
    Else{"Nothing happened"}
于 2016-12-28T19:19:45.683 回答