PowerShellを使ったファイル操作

マルウェアでPowerShellが使われる場合があると聞いたことがあったので、セキュリティの知識向上のためPowerShellの勉強（プログラミング）を始めました。何を作っていいかわからなかったので、とりあえず自分が使いそうなファイル操作のサンプルを試しに作りました。

#ファイル名の数値を同じ桁数にする

対象となるフォルダのツリー構造で表示します。

```:実行前
C:.
│  oya_1.txt
│  oya_10.txt
│  oya_11.txt
│  oya_2.txt
│  oya_3.txt
└─ko
    │  ko_1.txt
    │  ko_10.txt
    │  ko_11.txt
    │  ko_2.txt
    │  ko_3.txt
    └─mago
            mago_1.txt
            mago_10.txt
            mago_11.txt
            mago_2.txt
            mago_3.txt
```

ファイル名に数値を使っている場合、上のような順番に並んでしまい見栄えが良くない場合があります。数値を3桁に変更することにより、並び順を変えて見やすくします。

```powershell:プログラム
Get-ChildItem -Recurse | ForEach-Object {if ($_.Name -match '\d+') {
$n = $_.Name -split $Matches[0],2
Rename-Item -Path $_.FullName -NewName ($n[0]+'0'*(3-$Matches[0].Length)+$Matches[0]+$n[1])
}}
```

```:実行後
C:.
│  oya_001.txt
│  oya_002.txt
│  oya_003.txt
│  oya_010.txt
│  oya_011.txt
└─ko
    │  ko_001.txt
    │  ko_002.txt
    │  ko_003.txt
    │  ko_010.txt
    │  ko_011.txt
    └─mago
            mago_001.txt
            mago_002.txt
            mago_003.txt
            mago_010.txt
            mago_011.txt
```

#全角で書かれたファイル名を半角にする

対象となるフォルダをツリー構造で表示します。

```:実行前
C:.
│  ＴＥＳＴ１.txt
└─ＦＯＬＤＥＲ１
    │  ＴＥＳＴ２.txt
    └─ＦＯＬＤＥＲ２
            ＴＥＳＴ３.txt
```

ファイルとフォルダを全角から半角に変換します。

```powershell:プログラム
using namespace Microsoft.VisualBasic
Add-Type -AssemblyName 'Microsoft.VisualBasic'
Get-ChildItem -Recurse | ForEach-Object { Rename-Item -Path $_.FullName -NewName ([Strings]::StrConv($_.Name,[VbStrConv]::Narrow)) }
```

```:実行後
C:.
│  TEST1.txt
└─FOLDER1
    │  TEST2.txt
    └─FOLDER2
            TEST3.txt
```

#サブフォルダを含むファイル名の出力

子や孫フォルダを含むファイル名を出力します。csv形式で出力する場合と、フルパスのみ出力する場合の2パターンを記載します。なお、文字化け対策としてUTF-8でコード変換して出力しています。

```powershell:csv形式の出力
Get-ChildItem *.txt -Attributes Archive -Recurse | Select-Object DirectoryName,BaseName,Extension,Length,LastWriteTime | Export-Csv -Encoding UTF8 output.csv
```

```:output.csv（実行結果）
#TYPE Selected.System.IO.FileInfo
"DirectoryName","BaseName","Extension","Length","LastWriteTime"
"C:\Users\test\FOLDER1\FOLDER2","TEST3",".txt","10","2021/05/01 12:00:00"
"C:\Users\test\FOLDER1","TEST2",".txt","10","2021/05/01 12:00:00"
"C:\Users\test","TEST1",".txt","10","2021/05/01 12:00:00"
```

```powershell:フルパスのみの出力
Get-ChildItem *.txt -Attributes Archive -Recurse | ForEach-Object { $_.FullName } | Set-Content output.dat
```

```:output.dat（実行結果）
C:\Users\une\test3\FOLDER1\FOLDER2\TEST3.txt
C:\Users\une\test3\FOLDER1\TEST2.txt
C:\Users\une\test3\TEST1.txt
```

#ファイルに書かれているファイルを集める

先程出力したファイル`output.dat`を元に、ファイルを現在のパスにコピーします。なお、同じファイル名がある場合はコピーしません。

```powershell:プログラム
Get-Content output.dat | ForEach-Object { if (Test-Path $_) { Copy-Item -Path $_ -Destination . } }
```

```:実行結果
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        2021/05/01     12:00             10 TEST1.txt
-a----        2021/05/01     12:00             10 TEST2.txt
-a----        2021/05/01     12:00             10 TEST3.txt
```

#サブフォルダも含むファイル内容の検索

対象となるフォルダのツリー構造で表示します。

```:対象フォルダ
C:.
│  test1.txt
└─folder1
        test2.txt
```

```:test1.txt
ABCDEFGHIJ
1234567890
abcde12345
```

```:test2.txt
ABCDEF
123456
ABC123
```

`select-String`は正規表現も可能です。

```powershell:プログラム
Get-ChildItem -Recurse | Select-String -Pattern 'c\w' | Select-Object path,LineNumber,line
```

```:実行例
Path                                 LineNumber Line
----                                 ---------- ----
C:\Users\une\test4\test1.txt                  1 ABCDEFGHIJ
C:\Users\une\test4\test1.txt                  3 abcde12345
C:\Users\une\test4\folder1\test2.txt          1 ABCDEF
C:\Users\une\test4\folder1\test2.txt          3 ABC123
```

#おわりに

今回は（本当に初めてだったので）ファイル操作だけでしたが、次からはよりセキュリティに近いイベントログを集計したり、システムを自動操作できるスクリプトを組んでみたいと思います。