xlogI125’s blog

パソコン作業を効率化したい

Rename-Itemコマンドの入力準備をExcelシートで行う

ショートカットアイコンにドラッグ&ドロップされたファイルが入っているフォルダのファイル一覧をExcelシートに出力して保存する。

メモ

Rename-Item -LiteralPath 'C:\tmp\[A-Z]\A[123].txt' -NewName 'B4.txt'
  • 単一引用符を使うのでパスに ' が含まれる場合はパスの ' を '' に置換する
  • FileSystemInfo.Name を StrCmpLogicalW で比較して System.Array.Sort にて並べ替える

Excelシート例

旧 BaseName 旧 Ext 新 BaseName 新 Ext フォルダ -WhatIf # PowerShell
表題[X-Z];発行年'99 .txt 001 .txt C:\tmp\[A-Z] -WhatIf Rename-Item -WhatIf -LiteralPath 'C:\tmp\[A-Z]\表題[X-Z];発行年''99.txt' -NewName '001.txt'
表題X;発行年'99 .txt 002 .txt C:\tmp\[A-Z] -WhatIf Rename-Item -WhatIf -LiteralPath 'C:\tmp\[A-Z]\表題X;発行年''99.txt' -NewName '002.txt'
表題Y;発行年'99 .txt 003 .txt C:\tmp\[A-Z] -WhatIf Rename-Item -WhatIf -LiteralPath 'C:\tmp\[A-Z]\表題Y;発行年''99.txt' -NewName '003.txt'
表題Z;発行年'99 .txt 004 .txt C:\tmp\[A-Z] -WhatIf Rename-Item -WhatIf -LiteralPath 'C:\tmp\[A-Z]\表題Z;発行年''99.txt' -NewName '004.txt'

使い捨てスクリプト

  • エラー処理やCOMオブジェクトの解放を気にしていないので練習以外で実行しない
# [テスト環境]
#  PowerShell 5.1, Excel 2019, Windows 11 (2023年2月頃)
# [スクリプトファイル(.ps1)]
#  <ファイル名>
#   "%USERPROFILE%\Desktop\RenStr.ps1"
#  <エンコード>
#   UTF-8 (BOM付き)
# [ショートカット(.lnk)]
#  <リンク先(T)>
#   PowerShell.exe -NoLogo -NoExit -NoProfile -ExecutionPolicy RemoteSigned -File "%USERPROFILE%\Desktop\RenStr.ps1"
#  <作業フォルダー(S)>
#   ""

using namespace System.IO

Set-StrictMode -Version Latest

if ($args.Length -eq 0) {
  Write-Host "ショートカット(.lnk)にファイルをドラッグ&ドロップしてください"
  return
}

$pathDir = Split-Path -LiteralPath $args[0]

Set-Location -LiteralPath $pathDir

$gciFile = [FileSystemInfo[]]@(Get-ChildItem -File)

Write-Host "フォルダ`n$pathDir`n"

if ($gciFile.Length -eq 0) {
  Write-Host "フォルダにファイルがありません"
  return
} else {
  Write-Host "ファイル数`n$($gciFile.Length)`n"
}

Add-Type -Language CSharp -TypeDefinition @'
namespace MyNS {
  using System.Collections.Generic;
  using System.IO;
  using System.Runtime.InteropServices;

  public class CmpFSINameStrCmpLW : IComparer<FileSystemInfo> {
    public int Compare(FileSystemInfo x, FileSystemInfo y) {
      return StrCmpLogicalW(x.Name, y.Name);
    }

    [DllImport("Shlwapi.dll", CharSet = CharSet.Unicode)]
    private static extern int StrCmpLogicalW(
      [MarshalAs(UnmanagedType.LPWStr)] string psz1, 
      [MarshalAs(UnmanagedType.LPWStr)] string psz2
    );
  }
}
'@

[Array]::Sort($gciFile, [MyNS.CmpFSINameStrCmpLW]::new())

$xlWBATWorksheet = -4167

$xlApp = New-Object -ComObject Excel.Application
# $xlApp.DisplayAlerts = $false
# $xlApp.Visible = $true

$xlBooks = $xlApp.Workbooks
$xlBook = $xlBooks.Add($xlWBATWorksheet)
$xlSheets = $xlBook.Worksheets
$xlSheet = $xlSheets.Item(1)

$xlRangeSheetCells = $xlSheet.Cells

$xlRange = $xlRangeSheetCells.Item(1, 1)
$xlRange.Value = "'旧 BaseName"

$xlRange = $xlRangeSheetCells.Item(1, 2)
$xlRange.Value = "'旧 Ext"

$xlRange = $xlRangeSheetCells.Item(1, 3)
$xlRange.Value = "'新 BaseName"

$xlRange = $xlRangeSheetCells.Item(1, 4)
$xlRange.Value = "'新 Ext"

$xlRange = $xlRangeSheetCells.Item(1, 5)
$xlRange.Value = "'フォルダ"

$xlRange = $xlRangeSheetCells.Item(1, 6)
$xlRange.Value = "'-WhatIf"

$xlRange = $xlRangeSheetCells.Item(1, 7)
$xlRange.Value = "'# PowerShell"

for ($i = 0; $i -lt $gciFile.Length; $i++) {
  $r = $i + 2

  $xlRange = $xlRangeSheetCells.Item($r, 1)
  $xlRange.Value = "'" + $gciFile[$i].BaseName
  $xlRange.NumberFormatLocal = "@"

  $xlRange = $xlRangeSheetCells.Item($r, 2)
  $xlRange.Value = "'" + $gciFile[$i].Extension
  $xlRange.NumberFormatLocal = "@"

  $xlRange = $xlRangeSheetCells.Item($r, 3)
  $xlRange.Formula = "=A$r"
  $xlRange.NumberFormatLocal = "G/標準"

  $xlRange = $xlRangeSheetCells.Item($r, 4)
  $xlRange.Formula = "=B$r"
  $xlRange.NumberFormatLocal = "G/標準"

  $xlRange = $xlRangeSheetCells.Item($r, 5)
  $xlRange.Value = "'" + $gciFile[$i].DirectoryName
  $xlRange.NumberFormatLocal = "@"

  $xlRange = $xlRangeSheetCells.Item($r, 6)
  $xlRange.Value = "'-WhatIf"
  $xlRange.NumberFormatLocal = "@"

  $xlRange = $xlRangeSheetCells.Item($r, 7)
  $xlRange.Formula = "=`"Rename-Item `" & F$r & `" -LiteralPath '`" & SUBSTITUTE(E$r & `"\`" & A$r & B$r, `"'`", `"''`") & `"' -NewName '`" & SUBSTITUTE(C$r & D$r, `"'`", `"''`") & `"'`""
  $xlRange.NumberFormatLocal = "G/標準"
}

& {
  param($xlRangeSheetCells)
  $xlRangeSheetCellsColumns = $xlRangeSheetCells.Columns
  $xlRange = $xlRangeSheetCellsColumns.Item(7)
  $xlRangeFont = $xlRange.Font
  $xlRangeFont.Color = -16777056 # 赤160 緑0 青0
  $xlRangeFont = $null
  $xlRange = $null
  $xlRangeSheetCellsColumns = $null
  $xlRangeSheetCells = $null
} -xlRangeSheetCells $xlRangeSheetCells

$xlRange = $null
$xlRangeSheetCells = $null

$strMMddHHmmss = [DateTime]::Now.ToString("MMddHHmmss")
$pathFileSave = ${env:USERPROFILE} + "\Desktop\Myリネーム表" + $strMMddHHmmss + ".xlsx"

$xlSheet.SaveAs($pathFileSave)

$xlSheet = $null
$xlSheets = $null

$xlBook.Close()
$xlBook = $null
$xlBooks = $null

$xlApp.Visible = $false
$xlApp.Quit()
$xlApp = $null

$strInvokeItem = "Invoke-Item -LiteralPath '" + ($pathFileSave -replace "'", "''") + "'"

Write-Host "保存先のファイルを開く`n$strInvokeItem`n"

$strInvokeItem | Set-Clipboard

[GC]::Collect()
[GC]::WaitForPendingFinalizers()

Start-Sleep -Seconds 1.5

Write-Host @"
プロセスを取得
Get-Process | Where-Object Name -match 'Excel'
$(Get-Process | Where-Object Name -match 'Excel' | Out-String -Width 80)
"@