ほげほげー

C#メインで困ったことや面白く感じたことをメモしていきます。

PowerShellでgrepして置換みたいなことをしたい人生だった

PowerShellgrepコマンドはありません。

なのでコマンドレットを組み合わせてどうにかする必要があります。

順を追って試してみましす。

PowerShellGrepみたいな検索をする

Get-ChildItem -Path '対象のルートディレクトリパス' -Recurse | Select-String -Pattern '正規表現'

結果はこうなります

IgnoreCase : True
LineNumber : [該当行番号]
Line       : [該当行文字列]
Filename   : [ファイル名]
Path       : [ファイルパス]
Pattern    : [正規表現]
Context    : 
Matches    : {最初にマッチした部分文字列}

PowerShellで対象文字列を含むファイルの一覧を取得する

Get-ChildItem -Path '対象のルートディレクトリパス' -Recurse | ?{ Select-String -InputObject $_ -Pattern '正規表現' }

ここまでは順調。

PowerShellで文字列の置換をする

ファイルの中身を直接操作はできないので一旦ファイルから離れて文字列操作に。

$string -replace '正規表現', '置換文字列'

簡単。

余談ですがシングルクォートで囲って$1, $2, $3...を使うとグループ化した文字列を参照、展開できます。

ダブルクォートの場合でも$をバッククォートでエスケープすればできますが、まぁ面倒なので。*1

PowerShellでファイルに文字列を書き込む

Set-Content -LiteralPath 'ファイルパス' -Value '書き込む文字列'

できました。では大詰め、全部つなげてみます。

PowerShellGrepして置換っぽい事をする

$path = '対象のルートディレクトリパス'
$regex = '正規表現'
$replace = '置換文字列'
Get-ChildItem -Path $path -Recurse | ?{
    Select-String -InputObject $_ -Pattern $regex
} | %{
    $file = $_
    $content = Get-Content -LiteralPath $file
    Set-Content $_.FullName ($content -replace $regex, $replace)
}

できました。

へんなやりかたしてみる*2

$path = '対象のルートディレクトリパス'
$regex = '正規表現'
$replace = '置換文字列'
Get-ChildItem -Path $path -Recurse | ?{
    Select-String -InputObject $_ -Pattern $regex
} | %{ Invoke-Expression "`${$($_.FullName)} = `${$($_.FullName)} -replace `$regex, `$replace" }

どちらの方法も一度全部読み込んでから置換して入れ直してるのでOutOfMemoryに注意です。

後は関数化するなりなんなりしてProfileに登録しておけばいつでもGrepっぽいことができますね。

なんか他にいいやり方があったら是非教えていただきたいですホント。

*1:ダブルクォートで囲まれた範囲ではエスケープしないと普通の変数として展開されてしまう

*2:実はこの記事これがやってみたかっただけだったりしないでもない