前提環境
> &PSVersionTable
Name Value
---- -----
CLRVersion 2.0.50727.5485
BuildVersion 6.1.7601.17514
PSVersion 2.0
WSManStackVersion 2.0
PSCompatibleVersions {1.0, 2.0}
SerializationVersion 1.1.0.1
PSRemotingProtocolVersion 2.1
やりたいこと
起動演算子(&)経由の呼び出しであってもメッセージをそのまま出力したい
事象
起動演算子(&)を利用した時、メッセージの内容によっては正しく外部コマンドへ渡されない
例:
&でeventcreate.exeに引数としてメッセージを渡すscript
eventcreateTest.ps1
$cmd = "C:¥WINDOWS¥system32¥eventcreate.exe"
$severity = "ERROR"
$id = "1000"
$kind = "APPLICATION"
$msg = args[0]
$ret = & $cmd /T $severity /ID $id /L $kind /D $msg
if ($LASTEXITCODE -ne 0) {
throw "error! $LASTEXITCODE"
}
上記のとき、$msgが
"abcd" -> abcd
"ab cd" -> ab cd
"ab cd¥" -> ab cd" ## "がエスケープされてエラーが返却されて欲しい
"ab`tcd¥" -> ab[tab]cd" ## 同上
"abcd¥" -> abcd¥ ## ?!
"a¥bcd" -> a¥bcd
"a¥b cd" -> a¥b cd
## " -> ' でも結果は同じ
動きだけ見ているとC#コンパイラの解釈に沿った動きに見える
参考: http://www.msdn.microsoft.com/ja-jp/library/78f4aasd(VS.80).aspx
回避策
こんな処理を入れる
function escape($msg) {
if($msg.Contains(" ") -or $msg.Contains("`t")) {
$match = [Regex]::Match($msg, "¥¥+$")
$firstPart = $msg
$secondPart = ""
if($match.Success) {
## 末尾の¥の塊とそれ以外にわける
$firstPart = $msg.SubString(0, $match.Index)
$secondPart = $msg.SubString($match.Index)
## 末尾の¥を倍にする
$secondPart = $secondPart -replace "¥¥", "¥¥"
}
## メッセージ中の"や¥を文字列として表示したい場合のescape
$firstPart = $firstPart -replace "¥¥`"", "¥¥¥`""
$firstPart = $firstPart -replace "(?<!¥¥)`"", "¥`""
$msg = $firstPart + $secondPart
}
return $msg
}
上記でコマンド上の " は ¥" 末尾 n¥ は 2n¥ でエスケープしたメッセージとして渡される
あと ¥" や " も文字列としてエスケープされる。
正規表現の先読み/後読みって便利
結果
eventcreateTest.ps1
function escape($msg) {
if($msg.Contains(" ") -or $msg.Contains("`t")) {
$match = [Regex]::Match($msg, "¥¥+$")
$firstPart = $msg
$secondPart = ""
if($match.Success) {
## 末尾の¥の塊とそれ以外にわける
$firstPart = $msg.SubString(0, $match.Index)
$secondPart = $msg.SubString($match.Index)
## 末尾の¥を倍にする
$secondPart = $secondPart -replace "¥¥", "¥¥"
}
## メッセージ中の"や¥を文字列として表示したい場合のescape
$firstPart = $firstPart -replace "¥¥`"", "¥¥¥`""
$firstPart = $firstPart -replace "(?<!¥¥)`"", "¥`""
$msg = $firstPart + $secondPart
}
return $msg
}
$cmd = "C:¥WINDOWS¥system32¥eventcreate.exe"
$severity = "ERROR"
$id = "1000"
$kind = "APPLICATION"
$msg = escape(args[0])
$ret = & $cmd /T $severity /ID $id /L $kind /D $msg
if ($LASTEXITCODE -ne 0) {
throw "error! $LASTEXITCODE"
}
"abcd" -> abcd
"ab cd" -> ab cd
"ab cd¥" -> ab cd¥ ## OK
"ab`tcd¥" -> ab[tab]cd¥ ## OK
"abcd¥" -> abcd¥ ## OK
"a¥bcd" -> a¥bcd
"a¥b cd" -> a¥b cd