ファイル内のすべての文字列をPowerShellで置き換えるにはどうすればよいですか?


289

PowerShellを使用[MYID]して、特定のファイル内のすべての正確な出現箇所をに置き換えたいと思いますMyValue。これを行う最も簡単な方法は何ですか?


この質問への回答で提供されているよりもメモリ消費の点でより効果的なソリューションについては、「大きなファイルで検索して置換する参照してください。
Martin Prikryl

回答:


444

使用(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

3
ありがとう-「replace:[System.Object []]に 'replace'という名前のメソッドが含まれていないため、メソッドの呼び出しに失敗しました。」というエラーが表示されます。でも?
アマチュア

\私が発見したps v4でエスケープが機能するため。ありがとう。
ErikE 2013年

4
セット内容またはアウトファイルへ@robパイプ結果をあなたが変更を保存したい場合は
ロイックMICHEL

2
「[System.Object []]に 'replace'という名前のメソッドが含まれていないため、メソッドの呼び出しに失敗しました。というエラーが表示されました。V2しかないマシンでV3バージョンを実行しようとしたためです。
SFlag、2015年

5
警告:大きなファイル(数百メガバイト程度)に対してこれらのスクリプトを実行すると、かなりの量のメモリを消費する可能性があります。本番サーバーで実行する場合は、十分なヘッドルームがあることを確認してください
。D– neoscribe

89
(Get-Content file.txt) | 
Foreach-Object {$_ -replace '\[MYID\]','MyValue'}  | 
Out-File file.txt

括弧(Get-Content file.txt)が必要であることに注意してください:

括弧がない場合、コンテンツは一度に1行ずつ読み取られ、同じファイルに書き込もうとするout-fileまたはset-contentに到達するまでパイプラインを流れますが、get-contentによってすでに開かれているため、エラー。括弧により、コンテンツの読み取り操作が1回実行されます(オープン、読み取り、クローズ)。すべての行が読み取られたときにのみ、それらは一度に1つずつパイプ処理され、パイプラインの最後のコマンドに到達すると、ファイルに書き込むことができます。$ content = contentと同じです。$ content | どこ ...


5
可能であれば、賛成票を反対票に変更します。PowerShell 3では、これによりすべてのコンテンツがファイルからサイレントに削除されます!yuouのSet-Content代わりにを使用すると、「プロセスはファイル '123.csv'にアクセスできません。別のプロセスによって使用されているためです。」のOut-Fileような警告が表示されます。
Iain Samuel McLean Elder

9
get-contentが括弧内にある場合は発生しません。これにより、ファイルを開いたり、読み込んだり、閉じたりする操作が行われるため、発生するエラーは発生しません。サンプルテキストファイルでもう一度テストできますか?
Shay Levy

2
Get-Content括弧内のそれが動作します。かっこが必要な理由を回答で説明できますか?それはより安全Out-FileであるSet-Contentため、私はまだ置き換えます。かっこを忘れた場合にターゲットファイルが消去されるのを防ぎます。
Iain Samuel McLean Elder

6
ファイルエンコーディングUTF-8の問題。ファイルを保存するときに、エンコーディングを変更します。同じではありません。stackoverflow.com/questions/5596982/…set-contentはエンコーディングファイル(UTF-8など)を考慮していると思います 。アウトファイルで
Kiquenet、2015年

1
この解決策は不必要に誤解を招き、私がそれを使用したときに問題を引き起こしました。インストールプロセスですぐに使用される構成ファイルを更新していました。構成ファイルはまだプロセスによって保持されており、インストールは失敗しました。Set-Content代わりにを使用するOut-File方がはるかに優れており、より安全なソリューションです。申し訳ありませんが、反対票を投じる必要があります。
Martin Basista、2015

81

次の例に示すように、.NETのFileクラスとその静的メソッドの使用を好みます。

$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クラスを使用する場合のほとんど知られていないことは、PowerShellウィンドウで「[System.IO.File] ::」と入力したときに、Tabキーを押してメソッドをステップ実行できることです。


コマンドを使用してメソッドを表示することもできます [System.IO.File] | gm
fbehrens '10

なぜこのメソッドはからの相対パスを想定しているのC:\Windows\System32\WindowsPowerShell\v1.0ですか?
エイドリアン

そうですか?これはおそらく、.NET AppDomainがPowerShell内で開始される方法と関係があります。cdを使用すると、現在のパスが更新されない可能性があります。しかし、これは知識に基づく推測にすぎません。私はこれをテストしたり調べたりしませんでした。
rominator007 2016

2
これは、Powershellのバージョンごとに異なるコードを記述するよりもはるかに簡単です。
Willem van Ketwich、2016年

この方法も最速のようです。それを、前述の利点と組み合わせると、「なぜ他のものを使用したいのですか?」
DBADon

21

上記のものは「1つのファイル」に対してのみ実行されますが、フォルダ内の複数のファイルに対しても実行できます。

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

私は.xmlを使用したことに注意してください。ただし、.txtで置き換えることができます
John V Hobbs Jr

いいね。インナーを使用する代わりにforeachこれを行うことができますGet-ChildItem 'C:\folder\file*.xml' -Recurse | ForEach { (Get-Content $_).Replace('[MYID]', 'MyValue') | Set-Content $_ }
KCD

1
foreachGet-Contentは予期しないことを行うため、実際にはそのinnerが必要です...文字列の配列を返します。各文字列はファイル内の行です。:あなたが実行中のスクリプトとは別の場所にあるディレクトリ(およびサブディレクトリ)をループしている場合は、このような何かをお勧めします 修正したいファイルを含むディレクトリですが。Get-ChildItem $Directory -File -Recurse | ForEach { (Get-Content $_.FullName) | ForEach { $_ -replace '[MYID]', 'MyValue' } | Set-Content $_.FullName }$Directory
birdamongmen

1
「上の方」とはどのような答えですか?
Peter Mortensen 2017年

10

あなたはこのようなことを試すことができます:

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

7

これは私が使用するものですが、大きなテキストファイルでは遅くなります。

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

大きなテキストファイルの文字列を置換する場合で、速度が問題になる場合は、System.IO.StreamReaderおよびSystem.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を使用するより洗練された方法がおそらくありますが、それは良い出発点になるはずです。


set-contentはエンコーディングファイル(UTF-8など)を考慮していると思います。ではなくアウトファイルstackoverflow.com/questions/5596982/...
Kiquenet

2

PayetteのWindows Powershell in Actionから、あまり知られていないが驚くほどクールな方法を見つけました。$ env:pathと同様に、変数のようなファイルを参照できますが、中括弧を追加する必要があります。

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

ファイル名が次のような変数にある場合はどうなります$myFileか?
ΩmegaMan

@ΩmegaManうーん、これまでのところこれだけ$a = 'file.txt'; invoke-expression "`${c:$a} = `${c:$a} -replace 'oldvalue','newvalue'"
js2010

2

複数のファイルの文字列を置き換える必要がある場合:

ここに掲載されているさまざまなメソッドは、完了までにかかる時間に関して大幅に異なる場合があることに注意してください。私にとって、私は定期的に多数の小さなファイルを持っています。最もパフォーマンスの高いものをテストするために、5.52 GB(5,933,604,999バイト)のXMLを40,693の個別のファイルに抽出し、ここで見つけた3つの回答を実行しました。

## 5.52 GB (5,933,604,999 bytes) of XML files (40,693 files) 

#### Test 1 - Plain Replace
$start = get-date
$xmls = (Get-ChildItem -Path "I:\TestseT\All_XML" -Recurse -Filter *.xml).FullName
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
$xmls = (Get-ChildItem -Path "I:\TestseT\All_XML" -Recurse -Filter *.xml).FullName
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
$xmls = (Get-ChildItem -Path "I:\TestseT\All_XML" -Recurse -Filter *.xml).FullName
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
#>

問題は、複数のファイルではなく、特定のファイルの文字列を置き換えることでした。
PL

1

@ rominator007のクレジット

私はそれを関数にラップしました(もう一度使用したい場合があるため)

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

注:これは大文字と小文字を区別しません!!!!!

この投稿を参照してください:String.Replace ignoring ignore case


0

これは、PowerShellの現在の作業ディレクトリを使用して動作しました。FullNameプロパティを使用する必要があります。そうしないと、PowerShellバージョン5では機能しませんCSPROJ。すべてのファイルで、ターゲットの.NET Frameworkバージョンを変更する必要がありました。

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

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バージョンで私にとって最も効果的に機能したものです。

メジャー、マイナー、ビルド、リビジョン

5.1.16299.98


-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"}

3
StackOverflowへようこそ!書式設定を使用してください。ヘルプが必要な場合は、この記事を読むことができます。
CodenameLambda

1
これは真実ではありません。正しい答えを使用しても置換が見つからない場合、ファイルは書き込まれますが、変更はありません。例えばset-content test.txt "hello hello world hello world hello"、その後(get-content .\test.txt).Replace("something", "awesome") | set-content .\test.txt、この中で示唆したように、ファイルを空にしません。
Ciantic 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.