0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

PowerShellでExcelにファイル一覧を出力させる

Last updated at Posted at 2022-04-23

背景

PowerShellスクリプトでファイル一覧をExcelに出力させること自体はできていたのですが、出力内容をメンテナンスしたい事情(出力項目の位置調整、空白列の挿入など)があり、リファクタリングしてみたので投稿してみました。

PowerShellで実装したこと

ざっくりいえば、コマンドプロンプトでいうところのtreeコマンドっぽい出力結果をExcelに転記します。(実用するのはスクリプトの成果物(ファイル一覧Excel)の方です。)
ファイル一覧をExcelに出力しておけば、Windows10標準ファイルマネージャーの検索機能よりも一覧性に優れるし、柔軟かつ高速な検索(ファイル発掘ができるようになります。
もちろん、Excelなので検索結果の行数が多いと重たいし、リアルタイム性がないといった欠点もありますが、ユースケース次第では結構実用性があるかと思います。

  ※前提1:Windowsのファイルシステムでみえるディレクトリが対象です。Web(ファイルストレージ等)のディレクトリには非対応です(-_-;)
  ※前提2:バージョンはPowerShell 5.1

使い方

 1.コードをスクリプトファイル(.ps1)として保存する
 2.ショートカットをつくって、プロパティをごにょごにょする
 3.ショートカット経由でスクリプトを起動する
 4.コンソール画面で検索対象"ディレクトリ"のパスを入力しEnterする("ファイル"のパスだとエラーになる仕様です)

 ※3.の後に ↓ な感じなコンソール画面が起動します
demo1.PNG

検索したいディレクトリのパスを入力(コピペ)&Enter後、しばし待てば・・・
demo3.PNG

ファイル一覧Excelが完成!(検索対象ディレクトリ自体は出力されない仕様です)
demo5.png

 ※参考情報:自宅環境(Wifi経由でNASストレージを参照)にて、ディレクトリエントリが多いディレクトリに対して実行したところ...
  約5.17万件のエントリ → 約15分で完了(= エントリ取得 約4分 + Excel出力 約11分、ファイルサイズは3733KB)しました。
 -----
  21:52:00 ディレクトリからエントリを取得中...
  21:56:09 51738件のエントリをExcelに書き込み中...
  22:06:54 以下にファイル出力済み!
 -----
 が、リードタイムは環境依存要素が強かったハズなことをお含めおきください。(ボトルネックはストレージ?)
 職場環境(Wifi経由で共有ストレージ)だと、2~3万件のエントリ取得だけで1時間位かかるので、半年に1回位の頻度で休憩中に実行!~みたいな使い方をしています。(リアルタイム性がないのは妥協)

コード

ファイル一覧をExcelに出力する.ps1
# ディレクトリエントリを取得する
function StoreEntrysInHashArray
{
    param(
        $root_directory
    )

    $entrys = @()
    Get-ChildItem $root_directory -Recurse | 
    %{
        # 項目名の順序は保持される。取得不要なら#でコメントアウト (カスタマイズ可)
        $tmp = 
        [ordered]@{
            Name          = $_.Name
            IsDirectory   = $_.PSiscontainer
            Size_KB       = [Math]::Round(($_.Length / 1KB),2)
            Size_MB       = [Math]::Round(($_.Length / 1MB),2)
            #Dummy         = $null
            LastWriteTime = (Get-Date $_.LastWriteTime).ToString("yyyy/MM/dd HH:mm:ss")
            DirectoryName = $_.DirectoryName
            FullName      = $_.FullName
            FullName_lnk  = $_.FullName
        }
        $entrys += $tmp
    }
    
    Write-Output $entrys
    Return
}


# ファイル一覧をExcelで作る
function MakeFileListToExcel
{
    param(
        $entrys
       ,$dn_output = $PSScriptRoot
    )
    
    $fp_output = Join-Path $dn_output ("filelist_{0}.xlsx" -f (Get-Date).ToString("yyyyMMddhhmmss"))

    $xl = New-Object -ComObject Excel.application
    $wb = $xl.Workbooks.add()
    $ws = $wb.WorkSheets.item(1)
    $ktn = $ws.cells(1,1)


    # 連想配列の項目名を書き込む。ヘッダ行として
    $row_offset = $col_offset = 0
    [Array]$header_row = $entrys[0].keys

    $header_row |
    %{
        $ktn.offset($row_offset, $col_offset) = $_
        $col_offset += 1
    }

    $row_offset = 1


    # エントリを書き込む。項目名の末尾が"_lnk" かつ 実在パスの場合はハイパーリンク化
    $entrys | 
    %{
        $entry = $_
        $col_offset = 0

        foreach($col_name in $header_row) 
        {
            $ktn.offset($row_offset,$col_offset) = $entry.$col_name 

            if(($col_name -match ".*_lnk$") -and ($entry.$col_name -notin @("",$null)) -and (Test-Path($entry.$col_name)))
            {
                $ktn.offset($row_offset,$col_offset) = '=Hyperlink("{0}")' -f $entry.$col_name
            } 

            $col_offset += 1
        }

        $row_offset += 1
    }

    [void]$ktn.AutoFilter()

    $wb.saveas($fp_output)
    $xl.quit()
    $ws = $wb = $xl = $null
    [GC]::Collect()

    Write-Output $fp_output
    Return
}


# メイン処理はココから
$root_directory = Read-Host "検索対象のディレクトリを入力したらEnter [中断したい場合は Ctrl+C] `n"
if(-not ((Test-Path $root_directory) -and ((Get-Item $root_directory) -is [System.IO.DirectoryInfo])))
{
    Write-Output "実在するディレクトリを入力してください(>_<)"
    Exit
}


Write-Output ("`n{0} ディレクトリからエントリを取得中..." -f (Get-Date).ToString("HH:mm:ss"))
$entrys = @()
$entrys = StoreEntrysInHashArray -root_directory $root_directory
if($entrys.count -eq 0)
{
    Write-Output "エントリがありませんでした...┐(-ω-;)┌"
    Exit
}

Write-Output ("{0} {1}件のエントリをExcelに書き込み中..." -f (Get-Date).ToString("HH:mm:ss"),$entrys.count)
#$fp_filelist = MakeFileListToExcel -entrys $entrys -dn_output ("C:\Users\hoge\Desktop")
$fp_filelist = MakeFileListToExcel -entrys $entrys


Write-Output ("{0} 以下にファイル出力済み!`n{1}`n" -f (Get-Date).ToString("HH:mm:ss"),$fp_filelist)
Read-Host "出力ファイルを開きますか?[y/n]" | 
%{
    if($_ -eq "y"){Start-Process $fp_filelist}
}


Write-Output "オシマイ!! (/・ω・)/ =3"

参考にしたWebサイト

PowerShell で与えられたパスがフォルダかどうか判定する方法
PowerShell: orderedによる順序付けされた連想配列
PowerShellのHashtableの並び替えを制御したい
PowerShellにおける"戻り値"と"Return"について

参考図書

・PowerShell 5.1のお勉強にオススメです ※細かい誤字脱字を気にしない度量のある人向けですが...(・人-)
PowerShell実践ガイドブック クロスプラットフォーム対応の次世代シェルを徹底解説

以上

0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?