読者です 読者をやめる 読者になる 読者になる

いろいろな方法で PowerShell スクリプトを実行してみる【前編】

※本記事は PowerShell Advent Calendar 2012 の 11 日目です。

どういうわけかお誘いを頂いたので書いてみることにします。
本当は先週末にやろうと思っていたんですが WiiU に夢中になってすっかり忘れていました。

PowerShell、便利ですよね。何が便利って .NET Framework をそのまま使えるのがいい。
もうスクリプトを JavaScript とか MS-DOS バッチファイルなんかで書く気にはなれません。

というわけで、様々なタスクの自動処理に使われるわけですが、しばしば、よくわからないポイントでハマります。
PowerShell コンソールを立ち上げて、その中だけで完結する処理であればいいのですが、他のアプリから PowerShell を実行して処理をさせたい場合とかに困ります。
具体的にどういう場合かというと…やっぱりここで言うのはやめておきます。お察しください。

なお、俺は PowerShell を日頃からゴリゴリいじっているわけでもないので、詳しくありません。はっきり言って素人です。
そのため、「こんなことでハマりました」というのは書いているものの、「ハマらないためにはどうしたらいいか」はわかりません
きっと、日々PowerShell を操っている諸兄から見ると、当然の挙動なのやもしれません。
教えてエロい人。

というわけで、以下、本編(前編)です。

こんなスクリプトを用意します。単純に引数を1つずつ表示するだけです。

test1.ps1

'Length: ' + $args.Length

for ($i = 0; $i -lt $args.Length; ++$i)
{
    "[{0}]: {1}" -f $i, $args[$i]
}

まずはこれを、PowerShell コンソールから実行してみます。

call1.ps1

'(1) -----'
.\test1.ps1 a b c
'(2) -----'
.\test1.ps1 'a b' c
'(3) -----'
.\test1.ps1 "a b" c

結果はこうなります。当然ですね。

call1.ps1 結果

(1) -----
Length: 3
[0]: a
[1]: b
[2]: c
(2) -----
Length: 2
[0]: a b
[1]: c
(3) -----
Length: 2
[0]: a b
[1]: c

何も疑問を挟む余地はありません。

次にこれを、別の powershell.exe プロセスを立ち上げて実行してみます。

fork1.ps1 (A)

'(1) -----'
powershell.exe .\test1.ps1 a b c
'(2) -----'
powershell.exe .\test1.ps1 'a b' c
'(3) -----'
powershell.exe .\test1.ps1 "a b" c

結果はこうなります。…どうしたことでしょう。

fork1.ps1 (A) 結果

(1) -----
Length: 3
[0]: a
[1]: b
[2]: c
(2) -----
Length: 3
[0]: a
[1]: b
[2]: c
(3) -----
Length: 3
[0]: a
[1]: b
[2]: c

他にもいろいろ試してみましょう。

fork1.ps1 (B)

'(4) -----'
powershell.exe .\test1.ps1 'a b c'
'(5) -----'
powershell.exe .\test1.ps1 "a b c"
'(6) -----'
powershell.exe .\test1.ps1 "'a b' c"
'(7) -----'
powershell.exe .\test1.ps1 '"a b" c'

結果はこうなります。

fork1.ps1 (B) 結果

(4) -----
Length: 3
[0]: a
[1]: b
[2]: c
(5) -----
Length: 3
[0]: a
[1]: b
[2]: c
(6) -----
Length: 2
[0]: a b
[1]: c
(7) -----
Length: 3
[0]: a
[1]: b
[2]: c

(6)だけ差が出ました。

ちなみに(6)は

powershell.exe ".\test1.ps1 'a b' c"

としても、同じ結果になりました。

powershell.exe のコマンドラインオプションには -File というものもあります。
これをつけてみましょう。

fork1.ps1 (C)

'(8) -----'
powershell.exe -File .\test1.ps1 a b c
'(9) -----'
powershell.exe -File .\test1.ps1 'a b' c
'(10) -----'
powershell.exe -File .\test1.ps1 "a b" c
'(11) -----'
powershell.exe -File .\test1.ps1 'a b c'
'(12) -----'
powershell.exe -File .\test1.ps1 "a b c"
'(13) -----'
powershell.exe -File .\test1.ps1 "'a b' c"
'(14) -----'
powershell.exe -File .\test1.ps1 '"a b" c'

結果はこうなりました。

fork1.ps1 (C) 結果

(8) -----
Length: 3
[0]: a
[1]: b
[2]: c
(9) -----
Length: 2
[0]: a b
[1]: c
(10) -----
Length: 2
[0]: a b
[1]: c
(11) -----
Length: 1
[0]: a b c
(12) -----
Length: 1
[0]: a b c
(13) -----
Length: 1
[0]: 'a b' c
(14) -----
Length: 2
[0]: a b
[1]: c

-File のありなしで結果が全然違いますね。参った…。
とはいえ、概ね、そうなるだろうと(素人考えで)期待したように実行されているように思われます。
一点、(13) と (14) の結果が非対称なのが気になるところですね…。

最後に、MS-DOS のバッチ (.cmd) ファイルから実行した場合の結果も載せておきます。

call1.cmd

@echo off

echo (1) -----
powershell.exe .\test1.ps1 a b c
echo (2) -----
powershell.exe .\test1.ps1 "a b" c
echo (3) -----
powershell.exe .\test1.ps1 'a b' c
echo (4) -----
powershell.exe -File .\test1.ps1 a b c
echo (5) -----
powershell.exe -File .\test1.ps1 "a b" c
echo (6) -----
powershell.exe -File .\test1.ps1 'a b' c

こいつの結果は、今までのどれとも異なります。

call1.cmd 結果

(1) -----
Length: 3
[0]: a
[1]: b
[2]: c
(2) -----
Length: 3
[0]: a
[1]: b
[2]: c
(3) -----
Length: 2
[0]: a b
[1]: c
(4) -----
Length: 3
[0]: a
[1]: b
[2]: c
(5) -----
Length: 2
[0]: a b
[1]: c
(6) -----
Length: 3
[0]: 'a
[1]: b'
[2]: c

本記事はひとまず、ここでおしまいです。
が、実はまだ本題に入っていなかったりします。

しかしながら、アドベントカレンダーという性質上、日付が変わる前に掲載しないといけませんので、一旦区切らせて頂きます。
後編は、param を使ってパラメーターを宣言したファイルを外部から呼び出したりしてみます。配列型の引数とか使います。
WiiU とペーパーマリオに気を取られていなければ、きっと今週中には書くはずです。

# はてなブログって PowerShell のシンタックスハイライトには対応してないんですかね…