web-dev-qa-db-ja.com

特定の拡張子を持つすべてのファイルを検索および置換するPowerShellスクリプト

Windows Server 2008には、次のようにネストされたいくつかの構成ファイルがあります。

C:\Projects\Project_1\project1.config

C:\Projects\Project_2\project2.config

私の構成では、次のような文字列置換を行う必要があります。

<add key="Environment" value="Dev"/>

となります:

<add key="Environment" value="Demo"/>

バッチスクリプトを使用することを考えましたが、これを行う良い方法はありませんでした。PowerShellスクリプトを使用すると、簡単に実行できると聞きました。検索/置換の例を見つけましたが、C:\ Projectsディレクトリ内のすべてのフォルダーを走査し、拡張子が「.config」で終わるファイルを見つける方法を探していました。見つかったら、文字列値を置き換えてほしい。

これを行う方法や、洞察を提供できるPowerShellの達人を見つけるための良いリソースはありますか?

111
Brandon

ここで私の頭の上の最初の試み。

$configFiles = Get-ChildItem . *.config -rec
foreach ($file in $configFiles)
{
    (Get-Content $file.PSPath) |
    Foreach-Object { $_ -replace "Dev", "Demo" } |
    Set-Content $file.PSPath
}
160

PowerShellは良い選択です;)指定されたディレクトリ内のファイルを列挙し、それらを読み、処理することは非常に簡単です。

スクリプトは次のようになります。

Get-ChildItem C:\Projects *.config -recurse |
    Foreach-Object {
        $c = ($_ | Get-Content) 
        $c = $c -replace '<add key="Environment" value="Dev"/>','<add key="Environment" value="Demo"/>'
        [IO.File]::WriteAllText($_.FullName, ($c -join "`r`n"))
    }

読みやすいようにコードを複数の行に分割します。 [IO.File]::WriteAllTextの代わりにSet-Contentを使用できますが、最後に新しい行が追加されることに注意してください。 WriteAllTextを使用すると、回避できます。

それ以外の場合、コードは$c | Set-Content $_.FullNameのようになります。

30
stej

このアプローチはうまく機能します。

gci C:\Projects *.config -recurse | ForEach {
  (Get-Content $_ | ForEach {$_ -replace "old", "new"}) | Set-Content $_ 
}
  • 「古い」と「新しい」を対応する値に変更します(または変数を使用します)。
  • 括弧を忘れないでください。括弧がないと、アクセスエラーが発生します。
14
Tolga

私はxmlとxpathを使います:

dir C:\Projects\project_*\project*.config -recurse | foreach-object{  
   $wc = [xml](Get-Content $_.fullname)
   $wc.SelectNodes("//add[@key='Environment'][@value='Dev']") | Foreach-Object {$_.value = 'Demo'}  
   $wc.Save($_.fullname)  
}
9
Shay Levy

このPowerShellの例では、フォルダーとそのサブフォルダー内の文字列「\ foo \」のすべてのインスタンスを検索し、「\ foo \」を「\ bar \」に置き換え、文字列「\ foo \を含まないファイルを書き換えません"この方法では、文字列が見つからなかったファイルの最終更新日時スタンプを破棄しません。

Get-ChildItem  -Path C:\YOUR_ROOT_PATH\*.* -recurse 
 | ForEach {If (Get-Content $_.FullName | Select-String -Pattern '\\foo\\') 
           {(Get-Content $_ | ForEach {$_ -replace '\\foo\\', '\bar\'}) | Set-Content $_ }
           }
7
Kaye

@ Artyom のコメントを見つけましたが、残念ながら彼は答えを投稿していません。

私の意見では、これは受け入れられた答えの短いバージョンです。

ls *.config -rec | %{$f=$_; (gc $f.PSPath) | %{$_ -replace "Dev", "Demo"} | sc $f.PSPath}
7
M--

ファイル内のテキストを置き換えるための小さなヘルパー関数を作成しました。

function Replace-TextInFile
{
    Param(
        [string]$FilePath,
        [string]$Pattern,
        [string]$Replacement
    )

    [System.IO.File]::WriteAllText(
        $FilePath,
        ([System.IO.File]::ReadAllText($FilePath) -replace $Pattern, $Replacement)
    )
}

例:

Get-ChildItem . *.config -rec | ForEach-Object { 
    Replace-TextInFile -FilePath $_ -Pattern 'old' -Replacement 'new' 
}
5
Martin Brandl

再帰的な置換を行う場合、パスとファイル名を含める必要があります。

Get-ChildItem -Recurse | ForEach {  (Get-Content $_.PSPath | 
ForEach {$ -creplace "old", "new"}) | Set-Content $_.PSPath }

これは、現在のディレクトリのフォルダのすべてのファイルで、大文字と小文字を区別するすべての「古い」を「新しい」に置き換えます。

4