"treeコマンドのディレクトリ内のファイル一覧をソートしたい！"をPythonのsortedのkeyパラメータで

#はじめに
Windowsの話です。

`tree`コマンドは、ディレクトリの構造を木構造形式で見ることができるアレです。

```terminal:tree.cmd
├─CrystalBeadsCurtain00002
│  │  crystal_bead_curtain_diffuse.tga
│  │  crystal_bead_curtain_transparency.png
│  │  DemoProducts_CrystalBeadsCurtain00002_0001.max
│  │  DemoProducts_CrystalBeadsCurtain00002_0001.rfa
│  │  FRONT_LOD0_DemoProducts_CrystalBeadsCurtain00002.dwg
│  │  PLAN_LOD0_DemoProducts_CrystalBeadsCurtain00002.dwg
│  │  SIDE_LOD0_DemoProducts_CrystalBeadsCurtain00002.dwg
│  │
│  └─SOURCE_DATA
├─ENA101Art00001
│  │  DemoProducts_ENA101Art00001_0001.max
│  │  DemoProducts_ENA101Art00001_0001.rfa
│  │  DemoProducts_ENA101Art00001_render.jpg
│  │  FRONT_LOD0_DemoProducts_ENA101Art00001.dwg
│  │  PLAN_LOD0_DemoProducts_ENA101Art00001.dwg
│  │  SIDE_LOD0_DemoProducts_ENA101Art00001.dwg
│  │
│  └─SOURCE_DATA
│          EN_A_101_A_103_A104_190603.fbx
│
```

一覧性がいいので、必要なファイルが揃っているかなどを確認したいときに便利です。
自分は割とよく使います。
ただ、ちょっとだけ改善出来たらいいなと思うところがあります。
それが **並び順** についてです。


#Windowsのtreeコマンド
Windowsの`tree`コマンドは、Linuxの`tree`コマンドと違って、オプションが２つしかありません。
このオプションにソート(並び換え)に関するものはありません。
常に単純な名前順になってしまいます。

もっと見やすく並べられたらなぁというのが希望です。
出来ることなら、重要なファイルから順に並んでくれたらなおいいと思います。

これをPythonでなんとかします。


#ディレクトリの一覧を取得する
まずはディレクトリの一覧を作ります。
[こちらの方のブログ記事](https://enokitech.hatenadiary.org/entry/20091201/1259657213)を参考にしました。

## Powershellでディレクトリの一覧
当初、Powershellでそういうことが出来ないかとトライしてみました。
Windows10ではPowershellのウィンドウをとても簡単に "その場所" で開くことが出来ます。
そうするとパスの指定が不要になり、コマンドの実行が非常に簡単だからです。[^1]
[^1]: フォルダウィンドウのアドレスバーにPowershellと入力してEnterすれば、その場所で開きます。cdする必要がありません。ちなみにcmdと入力すればコマンドプロンプトが起動します。


ただ、自分がPowershellに慣れてなくて、結局諦めました...。

<details><summary>テストコード</summary><div>
```powershell:pstree.ps1
cls
function pstree($path, $level) {
    if ($path | Test-Path) {
        foreach($item in (Get-ChildItem $path) )
        {
            Write-Host ("`t"*$level + $item.Name)
            if($item.PSIsContainer)
            {
                pstree $item ($level+1)
            }
        }
    }
}
pstree "Z:\PROJECTS\MODELS" 0
```

</div></details>

そもそも、**並び替え**をどしたらよいのかわかりません。
リストを順番に見ていってスワップするような関数を書くとか？でしょうか。
なんか面倒くさそうな...。

でもPythonならもっと簡単に書けるはず！



## Pythonでディレクトリの一覧
Python3で書きました。
pathlibを使用します。


``` python:tree.py
from pathlib import Path

def tree(path=None, level=0):
    p = Path(path)
    if p.exists():
        ff = list(p.glob('*'))
        for f in ff:
            print( '{0}{1}{2}'.format('\n' if level == 0 else '' , '\t' * level, f.name.split('\\').pop() ) )
            if f.is_dir():
                tree(f, level + 1)

path = r'Z:\PROJECTS\MODELS'
tree(path, 0)
```
結果 ↓

```dart:_tree.txt
DFC8638BAA
	control.jpg    <--- テクスチャ画像は一番後ろへまとめて並べたい
	DEMO_DFC8638BAA_0001.max
	DEMO_DFC8638BAA_0001.rfa
	DEMO_DFC8638BAA_render.jpg
	DEMO_logo.tga   <--- テクスチャ画像は一番後ろへまとめて並べたい
	DR03-109-01-DFC79.jpg   <--- テクスチャ画像は一番後ろへまとめて並べたい
	FRONT_LOD0_DEMO_DFC8638BAA.dwg
	FRONT_LOD1_DEMO_DFC8638BAA.dwg
	lonseal_lonrium_5116.png   <--- テクスチャ画像は一番後ろへまとめて並べたい　dwgの間に入るなんてもってのほか！
	PLAN_LOD0_DEMO_DFC8638BAA.dwg
	PLAN_LOD1_DEMO_DFC8638BAA.dwg
	SIDE_LOD0_DEMO_DFC8638BAA.dwg
	SIDE_LOD1_DEMO_DFC8638BAA.dwg
	SOURCE_DATA
		DFC86-AA15-AA10_rev1.dwg
	SUS_diffuse.png
	SUS_glossiness.png
	SUS_normal.png
	top.jpg   <--- できれば拡張子ごとにきれいに並んでほしい
	fan_alpha.png
	fan_diffuse.png

DFD41AH18AA
	DEMO_DFD41AH18AA_0001.max
	DEMO_DFD41AH18AA_0001.rfa
	DEMO_DFD41AH18AA_render.jpg
	DEMO_logo.tga   <--- テクスチャ画像は一番後ろへまとめて並べたい
	FRONT_LOD0_DEMO_DFD41AH18AA.dwg
	FRONT_LOD1_DEMO_DFD41AH18AA.dwg
	PLAN_LOD0_DEMO_DFD41AH18AA.dwg
	PLAN_LOD1_DEMO_DFD41AH18AA.dwg
	SIDE_LOD0_DEMO_DFD41AH18AA.dwg
	SIDE_LOD1_DEMO_DFD41AH18AA.dwg
	SOURCE_DATA
		DFD41-AH18-AA00_rev2.dwg
		カタログ_P121.pdf
	top.jpg
```



普通にやると、やっぱり、**"いい感じ"** に並んでくれないです。 [^2]
[^2]: フォルダの中身はBIM、建築CG用のデータで、3Dモデルファイルとテクスチャ画像ファイルです。

この並び順を直すには、`glob`で集めたリストをソートしてやればいいわけです。


#sortedのkeyに自作の関数を指定する

ソートには`sorted` を使います。そして `key`パラメータに自作の関数を指定します。
無名関数を当てているのをよく見ると思いますが、`key`に関数を指定すると並び順を設定できます。

今回は、自分の意図した通りに並んでほしいので、自分ルールを定義します。
自分ルール用の関数の意図としては、"ファイルの種類ごとに並んでほしいんだけど、重要なファイルを先頭に持ってきて！" です。

```python:sort_by_my_rule.py
def sort_by_suffix(f):
    keys = [ 'max', 'rfa', 'dwg', 'dxf', 'jpg', 'jpeg', 'png', 'tga', 'psd', 'mb', 'ma', 'fbx', 'obj', 'fmz', 'fzb', 'bak', 'pdf', 'ms', 'txt', 'bat', 'vbs', 'log', 'zip', 'rar', 'xlsx', 'xls', '.swatches']
    values = list(range(0,len(keys)))
    d = dict(zip(keys, values))
    ext = Path(f).suffix[1:]
    if ext in keys:
        return d[ext]*10
    else:
        return 1000

```

拡張子の辞書を作って、インデックスを返すようにしました。[^3]
[^3]: 10倍してあるのは、あとでもっと細かいルールを追加する際、間に入れるため


## 比較できるものを返す関数を定義する
正直なところ、はじめ、どんな関数を作ればいいのかよくわからなかったのですが、
要は数字か文字みたいな**比較できるものを返せばいい**みたいです。



```python:sort_by_my_rule.py
ff = sorted( list(p.glob('*')) , key=sort_by_suffix )
```

このようにして出力すると

``` dart:_tree.txt
DFC8638BAA
	DEMO_DFC8638BAA_0001.max
	DEMO_DFC8638BAA_0001.rfa
	FRONT_LOD0_DEMO_DFC8638BAA.dwg
	FRONT_LOD1_DEMO_DFC8638BAA.dwg
	PLAN_LOD0_DEMO_DFC8638BAA.dwg
	PLAN_LOD1_DEMO_DFC8638BAA.dwg
	SIDE_LOD0_DEMO_DFC8638BAA.dwg
	SIDE_LOD1_DEMO_DFC8638BAA.dwg
	DEMO_DFC8638BAA_render.jpg
	control.jpg
	DR03-109-01-DFC79.jpg
	top.jpg
	lonseal_lonrium_5116.png
	SUS_diffuse.png
	SUS_glossiness.png
	SUS_normal.png
	fan_alpha.png
	fan_diffuse.png
	DEMO_logo.tga
	SOURCE_DATA
		DFC86-AA15-AA10_rev1.dwg
```

整理されて見やすくなりました。


より使いやすくするには、もうひと工夫必要ですが、とりあえず目的は達成しました。