[Powershell]汎用的に使えそうな設定ファイルとパーサ作ってみた


# 概要と目的

- ソース内のパラメータは設定ファイルにして外出ししたい
    - 具体的には、IPや名前などのリストを箇条書きで書いておきたい
- 設定ファイルは手動で簡単に編集できるようにしたい
    - 設定ファイル作るプログラムは書くのも使うのも面倒だから書きたくない
    - あまり書き方に制約は付けたくない
- 階層構造はあってもなくてもいい
- PowerShellでなんか遊びたかった（最重要）

最初はINIでいいかと思ったけど、配列が定義できないようなので却下
Yamlは良い感じだけど、Powershellがデフォルトで対応していなかったので却下
ただ、理想的なイメージはほぼYamlなのでYamlが使える環境ならYaml使えばいいと思う。
インデント面倒だけど。

最終的なコードはこれ
[https://github.com/sensq/home/blob/master/Get-ParametersListFromFile/Get-ParametersListFromFile.ps1](https://github.com/sensq/home/blob/master/Get-ParametersListFromFile/Get-ParametersListFromFile.ps1)

# 方針

- 一つのオブジェクトに、設定ファイルに書かれた文字列を変数名としたプロパティを作成していく
- 一塊の配列としたいリストの最初と最後に区切り用の文字列を書く
- コメントアウト記号以降は無視するようにする
- 行頭のコメントアウトは行ごと無視するようにする
- 説明や表示用に、名前も一緒に定義したい

```txt
[section:名称（任意）]
hoge
hoo
[/section]
```
の形式で書く。
※追記：
閉じる方の文字列は不要にしました。（コメント参照）
コードを参考にする場合は記事中のconf.txtではなく、Githubにコードと一緒に置いてあるconf.txtを参考にして下さい。

イメージの説明が難しいけど、とりあえず以下のようなファイルを読み込んでオブジェクト化するコードを書いた。
「$obj.curry.params」にカレーの材料が配列で入っている感じ。

```txt:conf.txt
[yamanote:山手線]
秋葉原
神田
東京
有楽町
新橋
[/yamanote]

[curry:カレーの材料]
ルー
じゃがいも
りんご
玉ねぎ
人参
#豚肉
牛肉
[/curry]

[diary_0821]
・家の掃除
・Powershell遊び
・qiitaの記事を書く
[/diary_0821]

[home_network:自宅IPリスト]
192.168.1.1 #メイン
192.168.1.2 #ノート
192.168.1.3 #無線AP
[/home_network]

[esx_ip:ESXのIP]
192.168.1.100
[/esx_ip]

[vm_ip:仮想環境IPリスト]
192.168.1.101
192.168.1.102
192.168.1.103
192.168.1.200
[/vm_ip]
[vm_pass:仮想環境パスワードリスト]
password
p@ssw0rd
pass
root
[/vm_pass]

[vm_cent6]
192.168.1.101
[/vm_cent6]

[vm_cent7]
192.168.1.102
192.168.1.103
[/vm_cent7]

[windows10]
192.168.1.200
[/windows10]
```

# 結果

実行コマンド

```ps1
$test = (.\Get-ParametersListFromFile.ps1 .\conf.txt)
$test.curry.params  #1
$test.vm_ip.params  #2
$test  #3
```

出力#1（$test.curry.params）

```txt
ルー
じゃがいも
りんご
玉ねぎ
人参
牛肉
```

出力#2（$test.vm_ip.params）

```txt
192.168.1.101
192.168.1.102
192.168.1.103
192.168.1.200
```

出力#3（$test）

```txt
＜自宅IPリスト＞
192.168.1.1 
192.168.1.2 
192.168.1.3 

＜山手線＞
秋葉原
神田
東京
有楽町
新橋

＜仮想環境パスワードリスト＞
password
p@ssw0rd
pass
root

＜vm_cent6＞
192.168.1.101

＜vm_cent7＞
192.168.1.102
192.168.1.103

＜windows10＞
192.168.1.200

＜仮想環境IPリスト＞
192.168.1.101
192.168.1.102
192.168.1.103
192.168.1.200

＜カレーの材料＞
ルー
じゃがいも
りんご
玉ねぎ
人参
牛肉

＜diary_0821＞
・家の掃除
・Powershell遊び
・qiitaの記事を書く

＜ESXのIP＞
192.168.1.100


Name                           Value                                                                                                                                                                                                                                                
----                           -----                                                                                                                                                                                                                                                
home_network                   @{name=自宅IPリスト; start_row=25; end_row=29; params=System.Object[]}                                                                                                                                                                                    
yamanote                       @{name=山手線; start_row=0; end_row=6; params=System.Object[]}                                                                                                                                                                                          
vm_pass                        @{name=仮想環境パスワードリスト; start_row=41; end_row=46; params=System.Object[]}                                                                                                                                                                               
vm_cent6                       @{name=vm_cent6; start_row=48; end_row=50; params=System.Object[]}                                                                                                                                                                                   
vm_cent7                       @{name=vm_cent7; start_row=52; end_row=55; params=System.Object[]}                                                                                                                                                                                   
windows10                      @{name=windows10; start_row=57; end_row=59; params=System.Object[]}                                                                                                                                                                                  
vm_ip                          @{name=仮想環境IPリスト; start_row=35; end_row=40; params=System.Object[]}                                                                                                                                                                                  
curry                          @{name=カレーの材料; start_row=8; end_row=16; params=System.Object[]}                                                                                                                                                                                      
diary_0821                     @{name=diary_0821; start_row=18; end_row=22; params=System.Object[]}                                                                                                                                                                                 
esx_ip                         @{name=ESXのIP; start_row=31; end_row=33; params=System.Object[]}  
```

# コード詳細

コード
[https://github.com/sensq/home/blob/master/Get-ParametersListFromFile/Get-ParametersListFromFile.ps1](https://github.com/sensq/home/blob/master/Get-ParametersListFromFile/Get-ParametersListFromFile.ps1)

## 詳細

- リストの取得
    - ファイルを読み込む
    - 開始行の行番号sと終了行の行番号eを保持しておく
    - 読みこんだファイルのs+1からe-1までループを回して行を取得する
- 開始行、終了行、コメントアウトの判断
    - 正規表現でパターンを決めておく
    - これだけ書き換えればフォーマットを変えられる
- オブジェクトに生成する変数の作り方
    - 開始行の[]内の文字列で作成する
    - [foo:bar]のようにコロンで句切られている場合は、fooで作成する
        - barは表示用でname変数に格納する
        - コロンが無い場合はfooをname変数に格納する

## メリット

- 一つのファイルにすべてのパラメータをまとめられる
- リストや箇条書きのパラメータを扱いやすい
- 一つの区切り内に一つだけパラメータを書けば、key=value形式っぽくも使える
- 記述する順番に依らない
- ファイル内の文字列で変数を作るから、コード無変更で汎用的に使いまわせる
- 設定ファイルにありがちな、「""で囲う、カンマで区切る、key=value形式で書く」などの地味に煩雑な制約が無い

## デメリット

基本的に実装が面倒だったから作らなかっただけ。

- 階層構造にできない
    - 開始行の書き方に制約入れれば作れそうだけど面倒（ドットで区切ってwhileで回すとか）
- セクション名が重複してるとエラーになる
    - 順番に名前保持していって、重複があったら警告出すとかすればできる
- 補完ができない
    - 上記の例だと、curryとかyamanoteとかの部分がなぜか補完候補に出てきてくれない
    - ```$test.curry.```まで書くと、```$test.curry.params```は補完してくれる
    - たぶんPowershellの知識不足
- yamlが使える環境ならほぼいらない子
    - 階層構造もできるし、ハッシュも作れるし
    - インデントとかハイフンが面倒なくらい
- 元々定義されているメソッド名やプロパティ名と同名の名前にしてしまうと不具合が生じる
    - Rank, Count, Length, Values, Add, Cloneなどが該当