この記事でわかること
PowerShellのforEachとは?
PowerShellはWindows 10に標準で入っている多機能なシェルスクリプトです。コマンドプロンプトとバッチファイルのパワーアップ版と言えます。そのPowerShellのforeachは繰り返し処理を行うことができて、PCの面倒な単純作業を自動化するのに最適のコマンドです。
PowerShellを実行する時は、スタートメニューにあるWindows PowerShell ISEを利用すると手軽で簡単です。なおサンプルはISE上ではそのまま実行できますが、ps1ファイルとして保存して右クリックで実行する場合は、管理者権限で以下を実行する必要があります。
Set-ExecutionPolicy RemoteSigned
これを行うことで、自分で作成したか又は署名付きのps1ファイルを実行できるようになります。
foreachのサンプル
以下はforeachの簡単なサンプルです。
$ary = @(1,2,3,’太郎’,’次郎’)
foreach($val in $ary){
Write-Host $val
}
実行すると以下のように配列の$aryの中身を表示します。foreachはこのように配列の中身に対して順次処理を行うことができるのです。
1
2
3
太郎
次郎
ForEach-Objectについて
PowerShellにはもう一つ、ForEach-Objectがあります。以下が上と同じ事を行うサンプルです。
@(1,2,3,’太郎’,’次郎’) | ForEach-Object{
Write-Host $_
}
|はパイプラインと言って、左の出力結果を右に受け渡すことができます。シェルスクリプトでは一般的なスタイルです。このサンプルでは配列の内容をForEach-Objectに渡します。ForEach-Objectの中では$_という変数に配列の個々の内容が代入されます。
二次元配列にも対応可能
foreachは二次元配列も扱えます。以下のようにすると、
$ary = @(
@(1,2,3,’太郎’,’次郎’),
@(4,5,6,’三郎’,’四郎’),
@(7,8,9,’花子’,’光子’)
)
foreach($val in $ary){
Write-Host $val
}
以下のように表示されます。処理される順番を押さえておきましょう。
1 2 3 太郎 次郎
4 5 6 三郎 四郎
7 8 9 花子 光子
foreachはファイル一覧も受け取れる
foreachはGet-ChildItemの結果も受け取ることができます。Get-ChildItemはファイル一覧の配列を返すコマンドです。以下のサンプルを実行すると、
foreach ($file in Get-ChildItem ‘c:\test’)
{
Write-Host $file
}
以下のようにc:\testにあるファイル一覧を返します。
test1.txt
test2.txt
foreachが受け取る配列の中身はファイルのオブジェクトのため、様々なことができます。例えば以下の場合、サイズが1MBを超えるファイルだけを表示します。
foreach ($file in Get-ChildItem ‘c:\test’)
{
if ($file.length -gt 1024KB) {
Write-Host $file ‘は 1MBを超えています。’
}
}
複数テキストファイルの文字列検索を行う
PowerShellのforeachを使ってWindowsのエクスプローラには無い機能を実現しましょう。UNIXではgrepという複数のテキストファイルの中身のワード検索をするコマンドがあります。以下がそれと同じ事を行うサンプルです。
foreach ($file in (Get-ChildItem ‘c:\test’).FullName)
{
$match = ‘PowerShell’
If(Select-String -Path $file -Pattern $match -Quiet) {
Write-Host $file ‘には’ $match ‘という文字が含まれています。’
}
}
foreachを使った文字列検索の解説
Get-ChildItemは.FullNameでファイル名だけではないフルパスを取得できます。Select-Stringは-Pathで指定したファイルの中に、$matchで指定された文字があるかどうかチェックします。-Quietを付けると、あるかどうかをTrue/Falseで返します。
Select-Stringの結果をIf文で判定し、結果がTrueならば$matchの文字列があったことを表示します。
フォルダを除外することもできる
ForEach-Objectを使って上のサンプルを実現する場合は以下のようになります。
(Get-ChildItem C:\test).FullName | ForEach-Object {
$match = ‘PowerShell’
If(Select-String -Path $_ -Pattern $match -Quiet) {
Write-Host $_ ‘には’ $match ‘という文字が含まれています。’
}
}
なおtestフォルダの中にフォルダがある場合、読み取れないというエラーが発生してしまいます。その場合はGet-ChildItemを以下のように直すとフォルダを除外することができます。
(Get-ChildItem C:\test | Where-Object { ! $_.PSIsContainer }).FullName
foreachを使わなくても実現できる
ところが実は、今回の処理はforeachを使わなくても実現可能だったりします。以下でもほぼ同じことが出来ます。
Select-String -Path C:\test\*.* -Pattern Powershell
ただしこの場合の出力は、指定した文字列を含む行が表示されます。上のサンプルのように「~と言う文字列がふくまれています」のようなメッセージは出ません。文字列検索以外の機能も付けたい場合は、上のようにforeachを使うのもよいでしょう。
PowerShellでファイルの名前変更をしよう
もう少し実用的なPowerShellサンプルを実行しましょう。例えば以下のような平成何年という名前の付いたファイルが複数あって、それを全て西暦に変えたファイルを複製したいとします。
file平成29年.txt
document平成30年.doc
表平成31年版.xls
手作業では手間がかかりますし、間違えるかもしれません。こういう時にPowerShellが役に立つのです。
ファイルの名前変更をするサンプル
以下のPowerShellサンプルでそれが実現できます。実行する場合は、c:\test\workに平成29年・平成30年・平成31年の3つのファイルを格納し、c:\test\work2フォルダを作成しておいてください。
$path = ‘c:\test\work’
$path2 = ‘c:\test\work2’
foreach ($file in Get-ChildItem $path)
{
if ($file -like ‘*平成29年*’) {
$oldName = $file.Name
$newName = $oldName.Replace(‘平成29年’,’2017年’);
Copy-Item $path\$oldName $path2\$newName
}
if ($file -like ‘*平成30年*’) {
$oldName = $file.Name
$newName = $oldName.Replace(‘平成30年’,’2018年’);
Copy-Item $path\$oldName $path2\$newName
}
if ($file -like ‘*平成31年*’) {
$oldName = $file.Name
$newName = $oldName.Replace(‘平成31年’,’2019年’);
Copy-Item $path\$oldName $path2\$newName
}
}
サンプルの解説
上のサンプルでは指定されたフォルダの中身をforeachで順次取り出して、-like演算子でファイル名をチェックします。*平成29年*の*はワイルドカードと言われ、0文字以上の任意の文字列と言う意味です。これでファイル名に平成29年が含まれるかどうかを判定します。
含まれていたらReplaceで西暦に置換し、Copy-Itemで別のフォルダにコピーします。平成30年や平成31年でも同じように行います。これを手作業でやると大変ですが、PowerShellなら全て自動でやってくれるのです。
Powershellのforeachを活用しよう
PowerShellのforeachを解説しましたが、ご理解頂けましたでしょうか。作業を自動化してくれてとても助かることが分かったと思います。VBAやC#でも同様のことはできますが、PowerShellはそれらとは違い、Windows 10の標準機能として入っている強みがあります。是非活用してください。
インフラエンジニア専門の転職サイト「FEnetインフラ」
FEnetインフラはサービス開始から10年以上『エンジニアの生涯価値の向上』をミッションに掲げ、多くのエンジニアの就業を支援してきました。
転職をお考えの方は気軽にご登録・ご相談ください。