PowerShell で DotNetZip を使用する ~ファイル名に日本語が含まれる場合の対応~

前回の記事(PowerShell で DotNetZip を使用する)で、PowerShelでZIPファイルを作成する方法について説明しました。

しかし、前回紹介した方法では、ファイル名に日本語が含まれている場合は文字化けを起こし、正常に解凍することができないファイルが作成されてしまします。

この問題を回避するには、New-Objectコマンドレットでオブジェクトを作成するときにエンコーディング(Shift_Jis)を指定します。

$Encoding = [System.Text.Encoding]::GetEncoding("shift_jis")
$zipfile = new-object Ionic.Zip.ZipFile($Encoding)

エンコーディングにShift_Jisを指定してファイルを圧縮する例は下記の通りです。

[System.Reflection.Assembly]::LoadFrom("c:\work\Ionic.Zip.dll")
$Encoding = [System.Text.Encoding]::GetEncoding("shift_jis")
$zipfile = new-object Ionic.Zip.ZipFile($Encoding)
#↓エンコーディングを指定すれば、日本語を含むファイル名であってもOK
$zipfile.AddFile("C:\Work\日本語を含むファイル名.xls","")
$zipfile.Save("C:\Work2\test.zip")$zipfile.Dispose()

PowerShell で DotNetZip を使用する

DotNetZipは、.NET Frameworkで簡単にZipファイルを操作するためのライブラリです。

PowerShellでも使用可能だということをnoraさんのブログ記事「.NETでZIPを操作するライブラリ・DotNetZip Library」にて教えていただきましたので、やってみました。

DotNetZipライブラリの入手についてはnoraさんのブログを参照ください。

 

DotNetZipライブラリを入手後、解凍してできた Ionic.Zip.dll が C:\Work に配置されている前提で話を進めます。適宜読み替えてください。

まずは、Ionic.Zip.dll を読み込みます。

[System.Reflection.Assembly]::LoadFrom("c:\work\Ionic.Zip.dll");

 

次に、New-Objectコマンドレットで ZipFileのインスタンスを作成します。

$zipfile =  new-object Ionic.Zip.ZipFile

 

あとは圧縮したいファイルやフォルダを追加します。

ファイルを追加する場合は AddFileメソッドを使用します。

AddFileめっそっどの第1引数は、作成するZipに含めるファイルを指定します。

第2引数は、Zip内に作成するフォルダ名を指定します。

この引数を指定すると、第1引数に指定したファイルが第2引数で指定したフォルダの中に作成されます。

たとえば、次のコードはMyLib.dll というファイルを、Zipファイル内にLIB1というフォルダを作成して配置します。

このようにしておくと、解凍されたときにLIB1というフォルダが作成され、その中にMyLib.dllが解凍されます。

$zipfile.AddFile("C:\Work\MyLib.dll","LIB1")

フォルダを追加する場合は、AddDirectoryメソッドを使用します。第1引数に圧縮したいフォルダを指定します。

次のコードはC:\Work2というフォルダを追加します。

$zipfile.AddDirectory("C:\Work")

圧縮するファイルやフォルダの追加が終わったら、Saveメソッドを使用して実際に圧縮ファイルを作成します。Saveメソッドの第1引数に保存ファイルパスを指定します。

次のコードはC:\Work2 の下に Test.zip という名前でZipファイルを作成します。

$zipfile.Save("C:\Work2\test.zip")

最後に、オブジェクトを破棄します。

$zipfile.Dispose()

上記をまとめたコードは下記の通りです。

[System.Reflection.Assembly]::LoadFrom("c:\work\Ionic.Zip.dll")
$zipfile =  new-object Ionic.Zip.ZipFile
$zipfile.AddFile("C:\Work\MyLib.dll","LIB1")
$zipfile.AddDirectory("C:\Work")
$zipfile.Save("C:\Work2\test.zip")
$zipfile.Dispose()

ながーい文字列を作ってテキストファイルに落とす方法

お久しぶりです。
最近ようやくPowerShell v2を触り始めました。(えー)
あのISEがよくできてていいですな。

 さて、今回もちょっとしたTipsを。というか試行錯誤を。v1の話です。

1GBのASCII文字列(なんでもいいんですが)をテキストファイルに書き込みたい。

まずためしたこと:

PS C:\Users\daisuke> Set-Content test.txt ("a"*1GB)
'*' 演算子が失敗しました:種類 'System.OutOfMemoryException' の例外がスローされました。
発生場所 行:1 文字:27+ Set-Content test.txt ("a"*1 <<<< GB)

いきなり1GBの文字列を発生させると、メモリが足りないって怒られました。環境にもよるんでしょうけどこれは正道ではなさそうです。

次に試したこと:

PS C:\Users\daisuke> 1..1GB|%{Add-Content test.txt "a"}
範囲の式が正しくありません。1073741823 は、範囲の最大サイズ (=50000 要素) を超えています。
発生場所 行:1 文字:4+ 1..1 <<<< GB|%{Add-Content test.txt "a"}

ループは5万回までしか回せないらしいです。むむむ。じゃあループ回数を減らそう。

PS C:\Users\daisuke> 1..1KB | %{Add-Content test.txt ("a"*1MB)}

うまく行きました!1GBのテキストファイルができあがりました。やった。

1MBも一回のループで書き込むのがいいのか分からないし、厳密にはちゃんとループ回数も考えてあげたほうがいいんだろうけど、1GB=1KB*1MBなのを利用してしまいました。

ただ、インデックスサーチ?かなんかが割り込みかけるみたいで、たまに

Add-Content : 別のプロセスで使用されているため、プロセスはファイル 'C:\Users\da
isuke\test.txt' にアクセスできません。
発生場所 行:1 文字:23
+ 1..1KB | %{Add-Content  <<<< test.txt ("a"*1MB)}

とか例外吐きます。ちゃんと排他制御も考えるとこれじゃダメっぽいです。ファイルにロックとかかけられないのかなー?

[Tips]指定したパラメータを持つコマンドレット一覧を取得する

訳あって、特定のパラメータを持つコマンドレットの一覧が取得したかったので、下記のような関数を作ってみました。(PowerShell V2 CTP3で動作確認済み)。 関数名はいまいちなので好みに合わせて変更してください。

function Get-XParamCmdlet([string]$paraname)
{
  $CmdCnt = 0
  Get-Command -CommandType cmdlet |
  %{
    $a = Get-Command $_.Name | select Parameters
    if ( $a.Parameters.Keys -contains $paraname)
    {
      $_.Name
      $CmdCnt += 1
    }
  }
  "$CmdCnt 個見つかりました"
} 

この関数は、引数にパラメータ名を指定します。

たとえば、Encodingというパラメータを持つコマンドレットは下記のようにして取得します。

PS> Get-XParamCmdlet "Encoding"
Add-Content
Export-Clixml
Export-Csv
Export-PSSession
Get-Content
Out-File
Select-String
Send-MailMessage
Sent-Content
9 個見つかりました

関数の中身についてちょっと補足します。

まずGet-Commandでコマンドレットのパラメータコレクションを取得します。

つぎに取得したコレクションに対して、-containsを使用して指定されたパラメータが含まれているかをif文で判断しています。

もう少し、スマートに書くことができるような気がするのですが、その辺はご自身で調整ください。

 

これで、特定のパラメータを持つコマンドレットを調べることができますので、ぜひお試しください。

[Tips]共有フォルダを作成する

ネタ元はこちら

PowerShellから共有フォルダを作成するにはどうするか? という質問がありました。

PowerShellのコマンドレットでは、自分が知る限りでは無理なので、net shareコマンドによる作成を考えました。

net shareコマンドについては

を参考にしました。

で、やり方ですが

PS> cmd /c 'net share ShareFolder=C:\Work /remark:"共有" /grant:everyone,full'

  のようにします。

この場合は

C:\Workというフォルダを Everyoneにフル権限をつけて共有することになります。

コメントは"共有"となります。

結局のところPowerShellでというよりは、PowerShellからCmd.exeにnet shareコマンド渡して実行したということです。

ちなみに net shareによる共有がサポートされているのはWindows Server 2003以降です。