PowerShell で連番リネーム


パイプされたファイル／フォルダに連番を振るコマンドレットです。[以前の記事](https://qiita.com/AWtnb/items/7b2a47204ac6d806b5ee) 同様に `-execute` オプション指定時のみリネームを実行するようにして誤操作に対する安全性を高めています。

### 環境

```
Name                           Value
----                           -----
PSVersion                      7.0.3
PSEdition                      Core
GitCommitId                    7.0.3
OS                             Microsoft Windows 10.0.18362
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
```

### 動作イメージ

#### 先頭に追加
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501098/e90746c8-1345-b5e1-a346-c0fb2bd21260.png)

#### 先頭に追加 & ファイル名統一
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501098/1973f64b-9045-7b3c-9a32-c013abf481ce.png)

#### 末尾に追加
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501098/c16d74ac-cb82-5226-02c0-c5ca5b02c9f7.png)

#### 末尾に追加 & ファイル名統一
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501098/99cfdf9f-e444-0e79-3a6d-68dae12622fb.png)

#### その他オプション
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501098/afa7fc81-76d7-77cd-7294-1c24337d506d.png)

ほかにもパラメータで以下を指定できます。

+ 連番をいくつから開始するか
+ いくつおきに連番を振るか
+ 連番を何桁でゼロ埋めするか


### コード

```powershell
function Rename-Index {
    <#
        .SYNOPSIS
        連番リネーム
        .DESCRIPTION
        パイプライン経由での入力にのみ対応（出力なし）
        .PARAMETER basename
        拡張子を覗いた部分のファイル／フォルダ名
        .PARAMETER start
        連番の開始番号
        .PARAMETER pad
        インデックスの桁数
        .PARAMETER execute
        指定した場合のみリネーム
        .PARAMETER step
        連番の増分
        .PARAMETER tail
        指定時はファイル名末尾に挿入
        .EXAMPLE
        ls * | Rename-Index
    #>
    param (
        [string]$basename,
        [int]$start = 1,
        [int]$pad = 2,
        [int]$step = 1,
        [switch]$tail,
        [switch]$execute
    )

    $proc = $input | Where-Object {$_.GetType().Name -in @("FileInfo", "DirectoryInfo")}

    $shiftJIS = [System.Text.Encoding]::GetEncoding("Shift_JIS")
    if ($basename) {
        $longestName = $proc.Name | Sort-Object {$shiftJIS.GetByteCount($_)} -Descending | Select-Object -First 1
        $fill = $shiftJIS.GetByteCount($longestName) + 1
    }

    $ANSI_BlackOnGreen = "`e[42m`e[30m"
    $ANSI_BlackOnWhite = "`e[47m`e[30m"
    $ANSI_Reset = "`e[0m"
    $bgColor = ($execute)? $ANSI_BlackOnGreen : $ANSI_BlackOnWhite

    $start -= $step
    $proc | ForEach-Object {
        $start += $step
        $index = "{0:d$($pad)}" -f $start
        $fileName = $_.Name
        if ($basename) {
            Write-Host $fileName -ForegroundColor DarkGray -NoNewline
            $leftIndent = $shiftJIS.GetByteCount($fileName)
            " {0}=> " -f ("=" * ($fill - $leftIndent)) | Write-Host -NoNewline

            $nameParts = ($tail)?
                @($basename, $index, $_.Extension):
                @("", $index, ($basename + $_.Extension))
        }
        else {
            $nameParts = ($tail)?
                @($_.Basename, "_$($index)", $_.Extension):
                @("", "$($index)_", $fileName)
        }
        "{0}$($bgColor){1}$($ANSI_Reset){2}" -f $nameParts | Write-Host

        if (-not $execute) {
            return
        }

        $renamedName = $nameParts -join ""

        try {
            $_ | Rename-Item -NewName $renamedName -ErrorAction Stop
        }
        catch {
            Write-Host ("ERROR: failed to rename '{0}' to '{1}'!" -f $fileName, $renamedName) -ForegroundColor Magenta
            (Test-Path $renamedName)?
                "(same name exists in directory)":
                "(other process may opening file)" | Write-Host -ForegroundColor Magenta
        }

    }
}
```

PowerShell 7 で三項演算子とエスケープシークエンス ``e` が導入されてかなり短く記述できるようになりました。