Help us understand the problem. What is going on with this article?

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

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
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away