Power Automate Desktopがなんとなく使える講座 #11 Excelのシート毎に別ファイルにして保存

（この記事は、2021年6月16日に執筆しました）

　第9回で、データをシート毎に振り分けましたが、今回は、シート毎に別のファイルを作成する方法を検討します。

# ◇普通にシートのデータを新しいエクセルブックにコピーする方法
　最初に思いつく方法は、1シート毎に新しいエクセルブックにデータをコピーしていく方法だと思います。しかし、この方法では、列幅やページ設定などはコピーされません。

<iframe width="560" height="315" src="https://www.youtube.com/embed/SzytXcyZUB0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

# ◇コピーした後でページ設定を行う方法
　コピーした後に、書式設定の処理を行わせます。これで問題ないのですが、コピー前の書式を再度設定しなおすというのは、スマートじゃないですね。時間もかかりますし。

<iframe width="560" height="315" src="https://www.youtube.com/embed/NdB4denfefY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

# ◇これが本命！Power Shellを使う方法
　PowerShellを使うと、エクセルのシートを別のエクセルブックにコピーすることが簡単に行えます。Power Shellのコードは以下になります。

```:PowerShel
## Excelを非表示で起動
$Instance = New-Object -ComObject Excel.Application
$Instance.Visible = $false

## ワークブックを開く
$InWB = $Instance.Workbooks.Open("%InFile%")

## ワークシート毎にワークブックを作成
foreach($CurrentWS in $InWB.Worksheets){
    $OutWB = $Instance.Workbooks.Add()
    $OutWS = $OutWB.Worksheets.item(1)
    $CurrentWS.Copy($OutWS)
    $OutWS.Delete()
    $OutWB.SaveAs("%SelectedFolder%\" + $CurrentWS.name + ".xlsx")
    $OutWB.Close()
}

## 終了処理
$Instance.quit()
$InWS = $null
$OutWS = $null
$InWB = $null
$OutWB = $null
$Instance = $null
[GC]::Collect()
```

　このコードをPower Automate Desktopの「PoserShellスクリプトの実行」の中に貼り付けます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/4ecf0ba0-310d-1bc8-3325-b2ff4bcb43c9.png)

　このスクリプトでは、元のエクセルファイルを**InFile**に、出力先のフォルダーを**SelectedFolder**に設定してから実行させます。

<iframe width="560" height="315" src="https://www.youtube.com/embed/TQP1A9RSQO8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

## 入力ファイルと出力先フォルダーをフローの中に書き込む
　入力ファイルと主知力先フォルダーの位置、名前が決まっているのであれば、変数の設定でInFileとSelectedFolderに設定してやれば、OKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/8c18d723-b8e6-8f15-b56d-b76033796ace.png)
![Untitled Diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/d5af8793-3038-576c-aa05-e5c055ee4f14.png)

## 違うPCでも動くようにしたい
　常に自分のPC上でだけ使うのならファイルパスの問題はないのですが、デスクトップのような特別なフォルダーを使う場合、PCによってファイルやフォルダーのパスが変わってしまいます。これを回避するには、アクションの変数にある「特別なフォルダーを取得」を使って、デスクトップのパスを変数**SpecialFolderPath**に設定します。表示は、自分のフォルダーのパスになっていますが、環境に合わせてパスは変更されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/b437cddb-c3ef-2354-62ae-86de8c9019d2.png)

　この変数を使ってInFileを設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/d7c83c49-ae5f-2d05-70ba-d289c108a38e.png)

**【重要】
　Power Automate Desktopでフローを作成する場合、ファイルやフォルダーを自分のデスクトップやドキュメントフォルダー、ピクチャフォルダーに設定する場合は、「特別なフォルダーを取得」を使用して、環境が変わっても動作するようにしておくことが大切です。**

　フローは、以下のようになります。
![Untitled Diagram-Page-2 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/91f12ea9-a44f-2218-faf3-77aaa48ce2d9.png)

## 出力先のフォルダーに以前のファイルがあると困る
　出力先に指定したフォルダーに、前回出力したエクセルブックが残っていると、以下のような確認メッセージが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/2db099ea-78e6-f287-69a7-258434565162.png)

　1ファイル毎に表示され、フローを停止してもPowerShellは止まらないのでかなり面倒です。事前に前のデータは削除しておきたいと思います。作業環境により、いくつかの方法があります。参考に3つの例を挙げておきます。

### (a)フォルダーを削除して、再度作成する方法
　**「フォルダーの削除」**で、対象フォルダを一気に削除して作成しなおします。全部消えるので、確実ですが、フォルダが存在しない場合はエラーとなります。エラー発生時の処理で、フロー実行を継続するようにしておけば、エラーで止まらずに次の処理に進みます。
　一番乱暴な方法です。

### (b)フォルダーを空にする方法
　**「フォルダーを空にする」**で、対象のフォルダー内のファイルとサブフォルダーを削除します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/9137d03d-7851-a50c-76be-39c96ede943a.png)

　フォルダーが存在しない場合はエラーが発生します。回避するには、このフローの前に**「フォルダーの作成」**を実行します。既に存在するフォルダーの場合はエラーとなりますが、エラー発生時に次のフロー実行を継続するようにしておけば、フォルダーが存在しない場合は新たに作成され、存在する場合は、フォルダーの中身が削除されるようになります。
　これも、(a)と同じレベルです。

### (c)ファイルを削除する方法
　**「ファイルの削除」**を使えば、フォルダー内のファイルを削除できます。この方法では、ワイルドカードを使用して削除できますので、エクセルファイルを選んで削除したり、ファイル名の一部を指定して削除することも出来ます。他のファイルが混在する可能性がある場合は、この方法を選択します。フォルダーが存在しない場合も考量するなら、(b)で行ったように、先にフォルダーの作成を実行しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/aee431fe-2ef7-14f3-5fb4-86c596e93414.png)
　(a)(b)に比べると、比較的丁寧な処理です。

### (d)フォルダー内にファイルがあったら処理を止める
　ＰCの前に人がいる場合は、これが一番安全な方法です。**「フォルダー内のファイルを取得」**を使い、アクションの条件から**if**を使って、ファイルの数を調べます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/02beacda-09d4-a134-92f4-679aaed8b46c.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/623c1a78-90bb-0045-ece2-cf008e2a3e9c.png)

　ただし、あまりにも控えめすぎるので、いつでも必ず実行させたい場合には、(a)～(c)の方法を使います。

### (e)Power Shellで処理する
　(a)～(d)の処理をPower Shellで書くことも出来ますが、フローを実行環境に合わせて変更するのは度々あることかもしれませんので、Power Automate Desktopのフロー上で行った方がわかりやすいと思い、敢えて今回は取り上げません。
　Power Shellからダイアログの表示もできますので、「[【PowerShell】OpenFileDialogでファイル選択ダイアログを使ってみる](https://buralog.jp/powershell-openfiledialog/)」などを参考に、Power Shellを試してみてください。

## 実行するときにファイルやフォルダーを選択できるようにしたい
　コードの中で、ファイル、フォルダの設定を書き込んでしまうと、違うファイル、別のフォルダーを選びたい場合は、その都度フローを変更しなければなりません。すべて自動化するのではなく、利用者が実行時に指定して動かせるようにしたい場合もあります。
　その場合は、アクションの中にある「**ファイルの選択ダイアログを表示**」と「**フォルダの選択ダイアログを表示**」を使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/28c45706-0125-2485-a26c-cbeab2cd3957.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/fb325aa1-842a-bde6-abe5-9efa8070ec0d.png)

　ファイルとフォルダー選択では、初期フォルダーはデスクトップにしています。また、ファイルフィルターは、エクセルの拡張子がxlsやxlsx、xlsmなどになっている場合でも選択できるように、ワイルドカードを使用して`*.xls*`としています。
　利用者が、自分で選択できる設計にした場合、ファイルの選択でキャンセルするかもしれませんし、選択されたフォルダが不適切なものかもしれません。分岐処理を使って、それぞれの条件の処理を記述してやる手間が必要です。
　業務のやり方や、環境によって使いやすく工夫してみてください。

## 完成フロー

<iframe width="560" height="315" src="https://www.youtube.com/embed/zNOF4BoYTYA" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/317d6969-eff5-d76c-abaa-90a37da7ecdd.png)

```
Folder.GetSpecialFolder SpecialFolder: Folder.SpecialFolder.DesktopDirectory SpecialFolderPath=> SpecialFolderPath
Display.SelectFile Title: $'''入力ファイルを選択してください''' InitialDirectory: SpecialFolderPath FileFilter: $'''*.xls*''' IsTopMost: True CheckIfFileExists: False SelectedFile=> InFile ButtonPressed=> ButtonPressed
IF ButtonPressed = $'''Cancel''' THEN
    EXIT Code: 0
END
Display.SelectFolder Description: $'''出力先のフォルダを選択してください''' InitialDirectory: SpecialFolderPath IsTopMost: True SelectedFolder=> SelectedFolder ButtonPressed=> ButtonPressed1
IF ButtonPressed1 = $'''Cancel''' THEN
    EXIT Code: 0
END
Folder.GetFiles Folder: SelectedFolder FileFilter: $'''*.xls*''' IncludeSubfolders: True FailOnAccessDenied: True SortBy1: Folder.SortBy.NoSort SortDescending1: False SortBy2: Folder.SortBy.NoSort SortDescending2: False SortBy3: Folder.SortBy.NoSort SortDescending3: False Files=> Files
IF Files.Count <> 0 THEN
    Display.ShowMessage Title: $'''エラーメッセージ''' Message: $'''フォルダー内にファイルがあるため、中止します。
空のフォルダーを選択してください。''' Icon: Display.Icon.ErrorIcon Buttons: Display.Buttons.OK DefaultButton: Display.DefaultButton.Button1 IsTopMost: True ButtonPressed=> ButtonPressed2
    EXIT Code: 0 ErrorMessage: $'''フォルダー内にファイルがあるため、中止します'''
END
System.RunPowershellScript Script: $'''# Excelを非表示で起動
$Instance = New-Object -ComObject Excel.Application
$Instance.Visible = $false

# ワークブックを開く
$InWB = $Instance.Workbooks.Open(\"%InFile%\")

# ワークシート毎にワークブックを作成
foreach($CurrentWS in $InWB.Worksheets){
    $OutWB = $Instance.Workbooks.Add()
    $OutWS = $OutWB.Worksheets.item(1)
    $CurrentWS.Copy($OutWS)
    $OutWS.Delete()
    $OutWB.SaveAs(\"%SelectedFolder%\\\" + $CurrentWS.name + \".xlsx\")
    $OutWB.Close()
}

# 終了処理
$Instance.quit()
$InWS = $null
$OutWS = $null
$InWB = $null
$OutWB = $null
$Instance = $null
[GC]::Collect()''' ScriptOutput=> PowershellOutput ScriptError=> ScriptError
```