powershell-yamlでYAMLファイルを読み込む


概要

powershell-yamlでYAMLファイルを読み込むときは、Get-Contentで読込んだ内容を改行コードで連結して文字列変数にしてから ConvertFrom-YAML する。

PS> $yaml = (Get-Content "MyTemplate.yaml") -join "`n"
PS> ConvertFrom-YAML $yaml

あるいは Get-Content 時に区切り文字にnull文字を指定することで1つの文字列として丸読みして、ConvertFrom-YAML する。

PS> $yaml = Get-Content "MyTemplate.yaml" -Delimiter "`0"
PS> ConvertFrom-YAML $yaml

詳細

問題

以下のように ConvertTo-YAML で生成(シリアライズ)した文字列を ConvertFrom-YAML で戻す(デシリアライズ)のは問題ない(勝手に型変換されてるのが問題なくもないけど)。

PS> $data = [ordered]@{"AWSTemplateFormatVersion"="2010-09-09";"Description"="MyTemplate.yaml"}
PS> $yaml = ConvertTo-Yaml $data
PS> $yaml
AWSTemplateFormatVersion: 2010-09-09
Description: MyTemplate.yaml

PS> ConvertFrom-Yaml $yaml
Name                           Value
----                           -----
AWSTemplateFormatVersion       2010/09/09 0:00:00
Description                    MyTemplate.yaml

このYAML文字列を Out-File (リダイレクトでもいい)でファイルに出力し、 Get-Content でファイルから読込んでから ConvertFrom-YAML で戻そうとすると、エラーになる。

PS> $yaml | Out-File "MyTemplate.yaml"
PS> Get-Content "MyTemplate.yaml"
AWSTemplateFormatVersion: 2010-09-09
Description: MyTemplate.yaml

PS> ConvertFrom-Yaml (Get-Content "MyTemplate.yaml")
ConvertFrom-Yaml: Cannot process argument transformation on parameter 'Yaml'. Cannot convert value to type System.String.

原因と回避方法

ConvertFrom-Yaml は変換対象に文字列をとるようになっている。ConvertTo-Yaml の出力は文字列型なので、これをそのまま渡す分には問題ない。

PS> $data = [ordered]@{"AWSTemplateFormatVersion"="2010-09-09";"Description"="MyTemplate.yaml"}
PS> $yaml = ConvertTo-Yaml $data
PS> $yaml.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

しかしファイル出力後に Get-Content で読込むと、一見元通りのYAML文字列が戻ってきているようだけれど、 Get-Content が1行1件の配列で読込結果を返している。

PS> $yaml | Out-File "MyTemplate.yaml"
PS> $yaml = Get-Content "MyTemplate.yaml"
PS> $yaml
AWSTemplateFormatVersion: 2010-09-09
Description: MyTemplate.yaml
PS> $yaml.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

普通の配列なので、改行文字でで連結すれば1つの文字列変数になり、ConvertFrom-Yaml でも戻せるようになる。

PS> $yaml = $yaml -join "`n"
PS> $yaml
AWSTemplateFormatVersion: 2010-09-09
Description: MyTemplate.yaml

PS> $yaml.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

PS> ConvertFrom-Yaml $yaml

Name                           Value
----                           -----
AWSTemplateFormatVersion       2010/09/09 0:00:00
Description                    MyTemplate.yaml

より簡潔に書くために

もちろん読込んだ時点で改行文字で連結してしまってもいい。

PS> $yaml = (Get-Content "MyTemplate.yaml") -join "`n"
PS> $yaml.GetType().Name
String

Get-Content の区切り文字にnullを指定してしまってもいい。

PS> $yaml = Get-Content "MyTemplate.yaml" -Delimiter "`0"
PS> $yaml.GetType().Name
String
PS> ConvertFrom-Yaml $yaml

Name                           Value
----                           -----
AWSTemplateFormatVersion       2010/09/09 0:00:00
Description                    MyTemplate.yaml

ファイルを丸読みするために区切り文字にnull文字を指定するというのは、Perlなどで使ったテクニックだけど、PowerShellでもこれでうまくいった。本来の目的がファイルを丸読みすることなのでこれが一番きれいで、ワンライナーにもまとめやすい。

PS> ConvertFrom-Yaml (Get-Content "MyTemplate.yaml" -Delimiter "`0")

Name                           Value
----                           -----
AWSTemplateFormatVersion       2010/09/09 0:00:00
Description                    MyTemplate.yaml

ただ個人的には、コーディング中にとっさにnull文字のエスケープシーケンスを思い出せる自信がない。

参考