概要
ファイルサーバ内のファイルをうち、特定の物を処理するスクリプトをPowerShellで作ろうと、Get-ChildItem *** -Recurse
したところ、階層が深くパスが260文字以上となるファイルがたくさんあることがわかった。
解決法は何通りかあるものの、robocopy
を使うのが簡単そうだったので、長いファイル名のフルパスを得る関数を作ってみた。
ついでに、長いパス名のファイルが無い場合でもGet-ChildItem
よりも2倍程度高速。
つくったもの
function Get-ChildFilePath {
[CmdletBinding()]
[OutputType([String[]])]
param (
[Parameter(Mandatory,ValueFromPipeline)]
[string]$Path
)
BEGIN {
$notexist = 'C:\DOESNOTEXIST';
while (Test-Path $notexist){
$notexist += Get-Random $(48..57+97..122) | % {[char]$_}
}
}
PROCESS {
if( Test-Path $Path ){
$list = robocopy.exe $Path $notexist /L /E /B /NP /FP /NS /NC /NJH /NJS /NDL |
ForEach-Object { $_.Trim() } | Where-Object { $_.Length -gt 0 }
@($list)
} else {
@()
}
}
}
※ PowerShellは管理者権限が必要
説明
robocopy
で使っているオプションの説明は下記(robocopy /?
から抜粋)。
/L :: リストのみ - いずれのファイルにも、コピー、タイムスタンプの追加、または削除を実施しません。
/E :: 空のディレクトリを含むサブディレクトリをコピーします。
/B :: バックアップ モードでファイルをコピーします。
/NP :: 進行状況なし - コピーの完了率を表示しません。
/FP :: 出力にファイルの完全なパス名を含めます。
/NS :: サイズなし - ファイル サイズをログに記録しません。
/NC :: クラスなし - ファイル クラスをログに記録しません。
/NDL :: ディレクトリなし - ディレクトリ名をログに記録しません。
/NJH :: ジョブ ヘッダーがありません。
/NJS :: ジョブ要約がありません。
以上のオプションとコピー先として存在しないフォルダを指定してrobocopy.exe
を呼び出すと、コピー元に指定したフォルダ以下にあるファイルの、フルパスのリストが出力されるので、無駄な空白を除いたものを出力する。
また結果が0行だったり1行だったりしたときに返り値の型が変わらないよう、@()
で囲んで必ず配列が返るようにした。
使い方
単純に指定したフォルダ以下の文字列のリストを得る。
Get-ChildFilePath "C:\Users\hogehoge\Documents"
Get-ChildItem
と同様にSystem.IO.FileSystemInfo
を得るには、Get-Item
に渡せばOK。
Get-ChildFilePath "C:\Users\hogehoge\Documents" | Get-Item
ファイル名でフィルタをかける場合は、先に文字列を条件に絞ってからGet-Itemをしたほうが高速(なので敢えてGet-ChildItem
とは互換のないようにした)。
Get-ChildFilePath "C:\Users\hogehoge\Documents" | ? { $_ -like "*.png" } | Get-Item