技術メモ、Tips、忘備録

技術系のメモ、試してみたこと、その他

Powershellで引数をMandatory(必須)にするときに地味にハマったこと

事象

Powershellでスペースありの文字列型の引数を受け取るとき、受け取った値にダブルクォーテーションがついてたりついてなかったりするので、それについて。

以下のようにparamを使って引数を受け取る自作関数があったとします。

function Get-Hoge{
    param(
        [Parameter(Mandatory)]
        [string]$arg
    )

Write-Host $arg
}

で、関数を呼ぶ時は以下の2つの呼び出し方があります。 渡すのはスペースを含む文字列とします。
パターン1

PS C:\Users\nogam> Get-Hoge -arg "has space"
has space

これは問題なさそうですね。
パターン2(Mandatoryにより入力待ちから入力する場合)

PS C:\Users\nogam> Get-Hoge
コマンド パイプライン位置 1 のコマンドレット Get-Hoge
次のパラメーターに値を指定してください:
arg: "has space"
"has space"

のほうは、ダブルクォーテーション付きで渡されます。
"has space"ではなくhas spaceとだけ入力すれば、ふつうにhas spaceが渡されます。

これを回避するためには、以下のようにTrimすればよいです。

Write-Host $arg.Trim("`"")

何がハマったのか

ここからはPowershellの仕様とは関係ない運用的な話になります。
例えば、上記の関数が以下のようにファイルパスを受け取るような関数だった場合を考えます。

function Open-HogeFile{
    param(
        [Parameter(Mandatory)]
        [string]$file_path
    )
    $file = [System.IO.File]::Open($file_path, [System.IO.FileMode]::Open)
    $file
    $file.Close()
}

パターン1の場合

PS C:\Users\nogam> Open-HogeFile -file_path "C:\Users\nogam\Downloads\hoge.txt"

CanRead        : True
CanWrite       : True
CanSeek        : True
IsAsync        : False
Length         : 11
Name           : C:\Users\nogam\Downloads\hoge.txt
Position       : 0
Handle         : 4228
SafeFileHandle : Microsoft.Win32.SafeHandles.SafeFileHandle
CanTimeout     : False
ReadTimeout    : 
WriteTimeout   : 

問題なさそうです。しかしパターン2だと

PS C:\Users\nogam> Open-HogeFile
コマンド パイプライン位置 1 のコマンドレット Open-HogeFile
次のパラメーターに値を指定してください:
file_path: "C:\Users\nogam\Downloads\hoge.txt"
"2" 個の引数を指定して "Open" を呼び出し中に例外が発生しました: "パスに無効な文字が含まれています。"
発生場所 C:\Users\nogam\Downloads\test.ps1:6 文字:5
+     $file = [System.IO.File]::Open($file_path, [System.IO.FileMode]:: ...
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ArgumentException
 
null 値の式ではメソッドを呼び出せません。
発生場所 C:\Users\nogam\Downloads\test.ps1:8 文字:5
+     $file.Close()
+     ~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) []、RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 

当然ですが失敗します。

ところで、「この引数の渡し方は失敗するのがわかっているのに、なぜそんな渡し方をするの?」と思いますよね。
それは、この関数の運用を考えると見えてきます。この関数の利用者は、
1.エクスプローラーからファイルを右クリック⇒パスをコピー
2.関数を実行するときに、コピーしたパスをペースト
という手順を踏むことが多くなるはずです。
上記の1.でコピーしたパスはダブルクォーテーション付きでコピーされます。
また、この関数を利用するのがPowershellをあまり利用しない人だった場合、関数実行時に指定する引数と対話型プロンプトから指定する引数で渡し方が違うとは想像しにくいです
なので、引数はTrimして受けるのがいい気がしますね。

ここまでお読みいただきありがとうございます。

・免責事項

当方は、当記事にコンテンツを掲載するにあたって、その内容、機能等について細心の注意を払っておりますが、コンテンツの内容が正確であるかどうか、最新のものであるかどうか、安全なものであるか等について保証をするものではなく、何らの責任を負うものではありません。また、当方は通知することなく当記事に掲載した情報の訂正、修正、追加、中断、削除等をいつでも行うことができるものとします。また、当記事、またはコンテンツのご利用により、万一、ご利用者様に何らかの不都合や損害が発生したとしても、当方は何らの責任を負うものではありません。