|||||||||||||||||||||

なんぶ電子

- 更新: 

PowerShellでファイルの読み書き

WindowsPowerShell

WSH(Windows Host Script)やコマンドプロンプトは、Windowsを操作するコードとして長年機能してきましたが、近年それらがPowerShellに置きかえられつつあります。

すでに潮流に乗り遅れた感も強いですが、ここで今までVBSで書いていたコードをPowerShellに置き換えるための覚書のひとつを残しておきます。

今回は主にファイルの読み書きです。

基本的な使い方

PowerShellのスクリプトは、拡張子「.ps1」で保存されます。「.vbs」や「.bat」ファイルはダブルクリックでスクリプトを実行することができましたが、「.ps1」スクリプトはそれができません。ダブルクリックすると標準では「メモ帳」でスクリプトの中身を表示します。実行したい場合は右クリックから「PowerShellで実行」を選択します。また、右クリックメニューで「編集」を選択すると今度はメモ帳ではなく「PowerShell ISE」という開発環境でスクリプトが開きます。

コード内でコメントを書きたい場合は、vbsでは'(クォート)だったものが、ps1では#(シャープ)に代わっています。

変数は$(ドルマーク)を付けて定義します。

vbsではメッセージボックスを標準で使えましたが、ps1では「Add-Type」を使って、「.NET」のフォームを利用して表示します。

Add-TypeではほかにもC#のソースコードを読み込んだりもできます。それらは「[読み込んだクラス]::メソッド」で実行できます。たとえば、HelloWorldというメッセージボックスを表示するには次のように書きます。

Add-Type -Assembly System.Windows.Forms [System.Windows.MessageBox]::Show('HelloWorld')

スクリプトのある場所をPowerShellで取得するには「$MyInvocation.MyCommand.Path」や、「$PSScriptRoot」という変数があります。

ファイルの読み書き

カレントディレクトリにある、input.txtから1行ずつ読み込みoutput.txtに出力するコードは次のようになります。

ReadWriteFileSample1.ps1

# Forms読み込み
Add-Type -Assembly System.Windows.Forms

#スクリプトの親ディレクトリを取得
$strAppDir = Split-Path -Parent $MyInvocation.MyCommand.Path
#出力ファイルパス
$strOutputFilePath = $strAppDir+"\output.txt"
#入力ファイルパス
$strInputFilePath = $strAppDir+"\input.txt"

#入力ファイル存在確認
if (!(Test-Path $strInputFilePath)) {
  [System.Windows.Forms.MessageBox]::Show("入力ファイル:"+$strInputFilePath+"が存在しません")
  return
}
#ファイルオープン
$fileI = New-Object System.IO.StreamReader($strInputFilePath,[System.Text.Encoding]::GetEncoding("sjis"));

#エラークリア(エラーは$Errorに格納されています)
$Error.Clear()
#ファイル出力開始上書き(sjisをUTF-8に変換して保存します)
Out-File $strOutputFilePath -Encoding utf8

# $nullはnullを表す自動変数です。
if ($Error[0] -ne $null) {
  #ファイルオープン失敗
  [System.Windows.Forms.MessageBox]::Show($Error[0])
  return
}

while(($strLine = $fileI.ReadLine()) -ne $null) {
  #書き込み
  echo "$strLine" | Out-File $strOutputFilePath -Encoding utf8 -Append
}
#ファイルクローズ
$fileI.Close()

[System.Windows.Forms.MessageBox]::Show("終了しました")

ファイル出力の方法はほかにもいろいろあって、リダイレクト>で指定する方法や「Start-TranScript」と「Stop-TranScript」を使う方法もあります。Stream-Readerで読み込んだのだから書き込みはStream-Writerだろう、と考える場合は次のようにします。Stream-WriterのコンストラクタはReaderと似ていますが、第二引数に自動変数の$trueを入れると追記、$falseを入れると上書きとなります。

ReadWriteFileSample2.ps1

Add-Type -Assembly System.Windows.Forms

$strAppDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$strOutputFilePath = $strAppDir+"\output.txt"
$strInputFilePath = $strAppDir+"\input.txt"

if (!(Test-Path $strInputFilePath)) {
  [System.Windows.Forms.MessageBox]::Show("入力ファイル:"+$strInputFilePath+"が存在しません")
  return
}
#入力ファイルオープン
$fileI = New-Object System.IO.StreamReader($strInputFilePath,[System.Text.Encoding]::GetEncoding("sjis"));
#出力ファイルオープン(ここでは上書きです)
$fileO = New-Object System.IO.StreamWriter($strOutputFilePath, $false,[System.Text.Encoding]::GetEncoding("utf-8"))

while(($strLine = $fileI.ReadLine()) -ne $null) {
  #書き込み
  $fileO.WriteLine($strLine)
}

#ファイルクローズ
$fileI.Close()
$fileO.Close()

[System.Windows.Forms.MessageBox]::Show("終了しました")

文字列の処理

このような処理の場合、途中読み込んだ文字列に対して何かしらの作業をすると思いますが、その際に使いそうなものをリストアップしておきます。

  • 文字長(.Length)

    文字長を取得したい場合は$strLine.Lengthを使います。

  • 切り出し(.Substring)

    文字列を字切り出したい場合は$strLine.Substring(n,m)を使います。引数は(開始位置、終了位置)で、一文字目は0となります。終了位置は省略すると残りの文字列すべてとなります。また存在しない位置を指定するとエラーとなります。

  • 分割(.Split)

    引数に指定した文字で分割します。戻り値は配列が返ります。たとえば、カンマで分割するなら次のようにします。$strLine.Split(',')

  • 正規表現(-match)

    正規表現による抽出は「$strLine -match "正規表現"」 とします。戻り値は一致すればTrueしなければFalseが返ります。一致した項目は$Matchesという変数に格納されます。

  • 文字置換(.Replace)

    文字列を置換するには$strLine.Replace(before,after)とします。

  • 正規表現で文字置換(-replace)

    $strLine -replace "正規表現","置換文字"とします。

ファイル取得

指定のフォルダ内のファイルをすべて取得して、順に処理したいという自動化のケースは多いと思います。その際はGet-ChildItemを用います。-Pathオプションの引数に取得したいディレクトリを指定します。

Get-ChildItem -Path d:¥

出力にフィルターをかけたい場合は-Filterオプションを使います。

Get-ChildItem -Path d:¥ -Filter *.TXT

Filterとは別に-Include、と-Excludeというオプションもあります。これらも対象の絞り込に使えますが、先に-Pathオプションで*(アスタリスク)を使って対象のディレクトリを指定しておく必要があります。たとえばd:¥test¥*とすると、dドライブのtestフォルダに対しての制限になります。-Pathオプションに*がない状態でこれらのオプションを使うと何も結果が返りません。

また公式のヘルプ(Get-Help Get-ChildItem -Online)によると、Filterオプションははオブジェクト取得時に機能するそうなので、ほかの絞り込みより効率的だということです。

他よく使いそうなオプションとしては、次のようなものがあります。

  • -Recurse

    サブフォルダを再帰的に検索します。

  • -File

    結果をファイルだけに絞り込みます。

  • -Directory

    結果をフォルダだけに絞り込みます。

  • -Name

    通常、結果にはファイルオブジェクトが戻りますが、-Nameオプションを使うことでファイル名だけを取得することも可能です。

取得した結果はforeachなどで処理します。

foreach($f in $files) {
  #ファイルのフルパスを表示(オブジェクト時)
  Host-Write($f.FullName)
}

筆者紹介


自分の写真
がーふぁ、とか、ふぃんてっく、とか世の中すっかりハイテクになってしまいました。プログラムのコーディングに触れることもある筆者ですが、自分の作業は硯と筆で文字をかいているみたいな古臭いものだと思っています。 今やこんな風にブログを書くことすらAIにとって代わられそうなほど技術は進んでいます。 生活やビジネスでPCを活用しようとするとき、そんな第一線の技術と比べてしまうとやる気が失せてしまいがちですが、おいしいお惣菜をネットで注文できる時代でも、手作りの味はすたれていません。 提示されたもの(アプリ)に自分を合わせるのでなく、自分の活動にあったアプリを作る。それがPC活用の基本なんじゃなかと思います。 そんな意見に同調していただける方向けにLinuxのDebianOSをはじめとした基本無料のアプリの使い方を紹介できたらなと考えています。

広告