programing

파일에서 발생하는 모든 문자열을 PowerShell로 대체하려면 어떻게 해야 합니까?

elecom 2023. 4. 23. 10:04
반응형

파일에서 발생하는 모든 문자열을 PowerShell로 대체하려면 어떻게 해야 합니까?

PowerShell을 사용하여 발생하는 모든 항목을 대체하고 싶다.[MYID]소정의 파일로MyValue가장 쉬운 방법은 무엇입니까?

사용(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

의 파일 클래스를 사용하는 것이 좋습니다.NET 및 그 스태틱 방식(다음 예 참조).

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

이것은 Get-Content와 같이 String-Array가 아닌 단일 String을 사용할 수 있는 장점이 있습니다.또, 대부분의 시간을 신경 쓸 필요 없이, 파일(UTF-8 BOM 등)의 인코딩을 처리합니다.

또한 Get-Content 및 Set-Content에 대한 파이프 스루를 사용하는 알고리즘과 달리 메서드는 회선 끝(사용 가능한 Unix 회선 끝)을 망치지 않습니다.

그래서 나는:몇 년 동안 부서질 수 있는 것들이 줄어들었어요.

사용할 때는 거의 알려지지 않은 것.NET 클래스는 [System](시스템)을 입력했을 때의 클래스입니다.IO.파일]::" 키를 누르면 PowerShell 창에서 메서드를 순서대로 볼 수 있습니다.

(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; $content | 여기서...

위의 파일은 "하나의 파일"에 대해서만 실행되지만 폴더 내의 여러 파일에 대해서도 실행할 수 있습니다.

Get-ChildItem 'C:yourfile*.xml' -Recurse | ForEach {
     (Get-Content $_ | ForEach  { $_ -replace '[MYID]', 'MyValue' }) |
     Set-Content $_
}

Payette의 Windows Powershell in Action에서 조금 알려졌지만 놀라울 정도로 멋진 방법을 찾았습니다.$env:path와 마찬가지로 변수와 같은 파일을 참조할 수 있지만 중괄호를 추가해야 합니다.

${c:file.txt} = ${c:file.txt} -replace 'oldvalue','newvalue'

다음과 같은 작업을 수행할 수 있습니다.

$path = "C:\testFile.txt"
$word = "searchword"
$replacement = "ReplacementText"
$text = get-content $path 
$newText = $text -replace $word,$replacement
$newText > $path

이것은 제가 사용하는 것이지만, 큰 텍스트 파일에서는 느립니다.

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

대용량 텍스트 파일의 문자열을 교체하는 경우 속도가 우려되는 경우 시스템 사용을 검토하십시오.IO.StreamReader 시스템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를 사용하는 보다 우아한 방법이 있을 수 있지만, 이 방법이 좋은 출발점이 될 것입니다.

@rominator007 크레딧

(다시 사용하고 싶을지도 모르기 때문에) 기능으로 포장했습니다.

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

메모: 대소문자를 구분하지 않습니다.!!

이 투고를 참조해 주세요.스트링무시 대소문자를 바꿉니다.

여러 파일의 문자열을 교체해야 하는 경우:

여기에 기재되어 있는 방법은 완료에 걸리는 시간에 따라 크게 다를 수 있습니다.저는 작은 파일을 많이 가지고 있어요.가장 성능이 뛰어난 것을 테스트하기 위해 40,693개의 개별 파일에서 5.52GB(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

이런 게 자주 나오니까 함수를 정의해놨어요.기본적으로는 대소문자를 구분하는 regex 기반 매칭이지만 리터럴 텍스트와 대소문자를 무시하는 스위치를 포함했습니다.

# 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

PowerShell의 현재 작업 디렉토리를 사용하여 이 작업을 수행할 수 있었습니다. 하다를 요.FullNamePowerShell 5개입니다.나는 목표를 바꿀 필요가 있었다. my NET의 CSPROJfiles.complete files files files files files files files files.

gci -Recurse -Filter *.csproj |
% { (get-content "$($_.FullName)")
.Replace('<TargetFramework>net47</TargetFramework>', '<TargetFramework>net462</TargetFramework>') |
 Set-Content "$($_.FullName)"}

특정 파일 이름의 모든 인스턴스에서 특정 행을 변경해야 했기 때문에 약간 오래되고 다릅니다.

ㅇㅇㅇㅇ.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 버전에서 가장 잘 작동한 것은 다음과 같습니다.

메이저. 마이너.Build. 리비전

5.1.16299.98

여기에는 여러 줄의 정규 표현식, 여러 파일(파이프라인을 사용), 출력 인코딩 지정 등을 지원하는 매우 간단한 방법이 있습니다.대용량 파일에는 권장하지 않습니다.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에 대한 요지로도 이용 가능합니다.

폴더 내의 모든 문자열을 바꾸는 예:

$path=$args[0]
$oldString=$args[1]
$newString=$args[2]

Get-ChildItem -Path $path -Recurse -File | 
ForEach-Object { 
    (Get-Content $_.FullName).replace($oldString,$newString) | Set-Content $_.FullName 
}

설정 내용 수 없는 " " " " 입니다.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"}

언급URL : https://stackoverflow.com/questions/17144355/how-can-i-replace-every-occurrence-of-a-string-in-a-file-with-powershell

반응형