17
20

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 5 years have passed since last update.

PowerShell で文字コードと改行コードの変換

Last updated at Posted at 2015-09-20

PowerShell - テキストファイルの文字コードを変換する方法 - Qiita のコード、便利そうだなーと思ったんですが、一方でファイルの取得やら、フォルダ構造を維持した出力やらを他のコマンドレットにやらせたいなーと思ったりもしました。具体的には次のように書きたいと思ったわけです。

  1. ls ./conv_enc -r | ConvertTo-WinContent

    • ./conv_enc 配下の UTF-8(改行コード:LF)のファイルを SJIS(改行コード:CR LF) のファイルに一括で変換
  2. cp ./win_enc/* -dest ./unix_enc -r -pass | ConvertTo-UnixContent

    • ./win_enc 配下の SJIS(改行コード:CR LF)のファイルを ./unix_enc にフォルダ構造を維持しつつコピーして UTF-8(改行コード:LF)のファイルに一括で変換

実装

PowerShell Windows形式⇔UNIX形式でのテキスト出力(改行コード調整) - YOMON8.NETも参考に、実装。

filter Convert-Encoding
{
  Param
  (
    [Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
    [String] $FullName,

    [ValidateSet("String","UTF8")] # 使いたい Encoding を適宜増やす
    [String] $FromEncoding,

    [ValidateSet("String","UTF8")] # 使いたい Encoding を適宜増やす
    [String] $ToEncoding,

    [String] $NewLineChar,
    [Switch] $PassThru
  )

  if (Test-Path $FullName -PathType Container) {return}

  $content = Get-Content $FullName -Encoding $FromEncoding

  if ($null -ne $content -and $content.length -gt 0)
  {
    $content -join $NewLineChar | Set-Content $FullName -Encoding $ToEncoding
    # V3 なら -Raw で読み込んで改行コードを置換したほうが良いかも
  }

  if ($PassThru) {$_}
}

filter ConvertTo-WinContent
{
  Param
  (
    [Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
    [String] $FullName,

    [ValidateSet("String","UTF8")] # 使いたい Encoding を適宜増やす
    [String] $Encoding = "UTF8",

    [Switch] $PassThru
  )

  $param = @{
    FullName     = $FullName
    FromEncoding = $Encoding
    ToEncoding   = "String"
    NewLineChar  = "`r`n"
    PassThru     = $PassThru
  }

  Convert-Encoding @param
}

filter ConvertTo-UnixContent
{
  Param
  (
    [Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
    [String] $FullName,

    [ValidateSet("String","UTF8")] # 使いたい Encoding を適宜増やす
    [String] $Encoding = "String",

    [Switch] $PassThru
  )

  $param = @{
    FullName     = $FullName
    FromEncoding = $Encoding
    ToEncoding   = "UTF8"
    NewLineChar  = "`n"
    PassThru     = $PassThru
  }

  Convert-Encoding @param
}

感想

PowerShellだと、とりあえず汎用的/抽象的な関数を作って特殊化/具体化するとき辛い気がします。Set-AliasArgumentListみたいな引数で部分適用できたら楽なのではとたまに思います。

あと、DirectoryInfoFileInfoオブジェクトにおいて、フルパスが格納されてるプロパティーはFullNameですが、ValueFromPipelineByPropertyNameを考えたら、FullNameのエイリアスプロパティとして、Pathを設定しておいてくれたら楽なのではとたまに思います。V3以降なら % fullname、V2以前なら %{$_.fullname}を噛ませばいいだけですが、やはりValueFromPipelineByPropertyNameを違和感なく使えたほうがオブジェクト指向とパイプラインがエレガントに融合できている感じがして良いと思うのです。

追記:2015/09/23

$PSBoundParameters

という自動変数を使うとラッパー関数が短く書けることを知りました。

filter ConvertTo-WinContent
{
  Param
  (
    [Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
    [String] $FullName,

    [ValidateSet("String","UTF8")] # 使いたい Encoding を適宜増やす
    [String] $FromEncoding = "UTF8",

    [Switch] $PassThru
  )

  Convert-Encoding -ToEncoding String -NewLineChar "`r`n" @PSBoundParameters
}

filter ConvertTo-UnixContent
{
  Param
  (
    [Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
    [String] $FullName,

    [ValidateSet("String","UTF8")] # 使いたい Encoding を適宜増やす
    [String] $FromEncoding = "String",

    [Switch] $PassThru
  )

  Convert-Encoding -ToEncoding UTF8 -NewLineChar "`n" @PSBoundParameters
}

Proxy Function

ラッパー関数を書くためのメタい方法があるらしく、次のようにも書けるっぽいです(正しいコードなのか謎)。

$tMeta  = [System.Management.Automation.CommandMetaData]
$tProxy = [System.Management.Automation.ProxyCommand]

$newFunction = {
  $Metadata = New-Object $tMeta (Get-Command Convert-Encoding)
  [void] $MetaData.Parameters.Remove("ToEncoding")
  [void] $MetaData.Parameters.Remove("NewLineChar")
  $sb = $tProxy::Create($Metadata) -replace $default -replace $cmd
  Invoke-Expression "function ${name} {${sb}}"
  $sb
}

$name    = 'ConvertTo-WinContent'
$default = @('{FromEncoding}', '{FromEncoding} = "UTF8"')
$cmd     = @(
  'wrappedCmd @PSBoundParameters',
  'wrappedCmd -ToEncoding String -NewLineChar "`r`n" @PSBoundParameters'
)
. $newFunction

$name       = 'ConvertTo-UnixContent'
$default[1] = '{FromEncoding} = "String"'
$cmd[1]     = 'wrappedCmd -ToEncoding UTF8 -NewLineChar "`n" @PSBoundParameters'
. $newFunction
17
20
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
17
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?