PowerShell
開発環境: PowerShell V1.0
PowerShell とは
PowerShell
とは、システム管理に役立つように設計されたスクリプト言語です。Windows Server
2008からは、標準で搭載されるようです。基本的には、.Net Framework をベースとしたスクリプト言語であり、Unix
のShell のようなコマンドラインベースでのスクリプトが書ける。また、パイプで出力をつなぐこともできる。しかし、Unix
のShell はテキストベースで扱われるのに対して、PowerShell
オブジェクトでの受け渡しが可能となっている。このため、かなり柔軟なデータを扱うことができる様になっている。
個人的な感想としては、確かに Object をパイプで渡せて、.NET Framework
も使える、Com も使える、スクリプトとしても実行できる、C#
に似ているとなると、結構いい感じに聞こえますが、現在の欠点として、無償の開発環境がない(有料のものはある)ので、手間がかかる。Visual
Studio の
インテリセンスに慣れてしまった身としては、とても辛いです。また、文法も似ているようで、違うので、とまどいます。たとえば、function
Add (int x, int y) { x + y} に対して、 Add 1 2 で、3 が帰るが、Add(1, 2) では
1 2 が帰る。エラーならエラーとして欲しい。また、オブジェクトに $x.abc
みたいな存在しない名前を指定してもエラーにならない。どうしてそうなっているのかよくわからないけれど、バグの原因になるのでやめて欲しい。それから、elseif
も else if だとエラーになる。文字列のエスケープも C#
とちょっとお作法が違う。また、Excel.Application
との連動に関しても、Openが使えないという問題があります。ということで、ちょっとしたものを作ろうと思っても、初めてPowerShell
を触る人は、かえって慣れている言語で開発した方が速かったりするかもしれません。業務で使うなら、もうすこし、こなれるのを待った方がいいというのが正直な感想です。
インストール
PowerShell は、Microsoft
よりダウンロードが可能であり、現在りりーすされているバージョンは、V1.0です。
V2.0 CTP もダウンロード可能です。V2.0
CTPでは、簡単なエディタがついているので、そちらを使った方が楽かもしれません。ただし、ヘルプは英語です。また、そのままではヘルプが表示されないので、ヘルプファイルのディレクトリ名をja-JPに変更する必要があります。
このあたりの注意は、Shigeya
Tanabe's blog あたりを参照するとよいでしょう。
変数
変数は、$x = "abc"
という形です。型宣言はありませんが、型は自動的に判別され、型付きのオブジェクトとして扱われます。
文字列のエスケープ
ダブルクオート内のダブルクオート
$t="!""#$%&'()=-+*~^\" "を2つ重ねる。
シングルクオート内のシングルクオート
$t='!"#$%&''()=-+*~^\'
'を2つ重ねる。 ''
文字のエスケープ
改行は `n 逆アポストロフィ
`0 `a `b `f `n `r `t
`v
シングルクオートと、ダブルクオートの違い
ダブルクオート内の変数は評価される。
$a="test"
"$a"
test
シングルクオート内の変数は評価されない。
$a="test"
'$a'
$a
変数の削除
$matches=$null
というように、$null を代入するか、次のように Remove-Variable
コマンドレットにより削除します。
Remove-Variable x
注意 Remove-Variable $x とすると、"Remove-Variable : 名前 'x'
の変数が見つかりません。" というエラーになる。
$x ではなく、x。
変数のスコープ
$global:x="abc"
#すべてのスコープからアクセスできる
$script:x="abc"
#スクリプトファイル内のすべてのスコープからアクセスできる
$local:x="abc"
# 現在のスコープと、その子の内部からのみアクセスできる
$private:x="abc"
# 現在のスコープからのみアクセスできる
指定しない場合は、private
制御構造
foreach文
Foreach-Objectを使う場合
$a="123","234","324","456","567"
foreach-object -InputObject $a -Process {if ($_ -eq "324")
{write-output "Equal" $_} }
$a | foreach-object -Process {if ($_ -eq "324") {write-output
"Equal" $_} }
foreach を使う場合
$a="123","234","324","456","567"
foreach ($line in $a) {...}
ともかける。
ファイルを1行ごとに処理
$filename="./test.txt"
$regex =[regex] "test string"
foreach ($line in $(Get-Content $filename))
{
$matches = $regex.Matches($line);
if($matches.Count -gt 0)
{
write-output
"====================================="
write-output $line
write-output
"------------------------------------------------------------------"
$matches[0].Value
}
}
if 文
注意 else if ではなく、 elseif
else と if の間のスペースがあると
"'else' キーワードの後にステートメント ブロックが存在しません。"というエラーになる。
if ()
{
}
elseif ()
{
}
else
{
}
演算子
Where-Object
演算子 |
説明 |
-eq |
等しい |
-ne |
等しくない |
-gt |
より大きい |
-ge |
以上 |
-lt |
より小さい |
-le |
以下 |
-like |
ワイルドカードによる比較 |
-notlike |
ワイルドカードによる比較 |
-contains |
含む "abc", "cde", 123 -contains "cde" |
-notcontains |
含まない |
-match |
正規表現による比較 |
-nomatch |
正規表現による比較 |
-and |
論理積 |
-or |
論理和 |
-not |
論理否定 |
! |
論理否定 |
大文字と小文字は区別しない。大文字と小文字を区別する場合は、演算子名の前に文字cを付けて、たとえば -ceq のようにします。
-like演算子
ワイルドカードによる曖昧検索を行います。曖昧検索にヒットした場合はTrueを返します。
-match, -notmatch 演算子
正規表現による検索を行います。マッチした場合はTrueを返します。ただし、あとで述べる様にバグがあるようなので注意。
関数(function)
function Add($x, $y)
{
return ($x+$y)
}
Add 1 2
3
Add(1,2) はダメ!!
1
2
1と2が返されてしまう。紛らわしい!コーディング時のエラーのもとになる。
関数のスコープ
add.ps1というファイル
function global:GlobalAdd($x, $y)
{
return $x+$y
}
function script:ScriptAdd($x, $y)
{
return $x+$y
}
./add.ps1
GlobalAdd 1 2
3
ScriptAdd 1 2
スクリプトのスコープで定義された関数なので、スクリプトの外では存在しない。"用語 'ScriptAdd'
は、コマンドレット、関数、操作可能なプログラム、またはスクリプト
ファイルとして認識されません。用語を確認し、再試行してください。"というエラーになる。
スクリプトファイル
'$args[0] : ' + $args[0]
'$args[1] : ' + $args[1]
'$args[2] : ' + $args[2]
'$argn : ' + $args.Count
ここで、"$args[0] : " + $args[0] とダブルクオートにすると、$argsが評価されてしまうので、注意。
スクリプトファイルの拡張子
拡張子は、「ps1」。例: test.ps1
システム情報の取得
プロセス情報
Get-Process | Get-Member
Get-Process | Get-Member -MemberType Properties
Get-Process -Name powershell | Format-List
Get-Process | Out-Host -Paging | Format-List
WMI
Get-WmiObject -Class Win32_LogicalDisk
Get-WmiObject -Class
Win32_LogicalDisk | Select-Object -Property Name,FreeSpace|
Get-Member
イベントログ
New-Object -TypeName
System.Diagnostics.EventLog
サービス情報
Get-Service
Get-Service | get-member
良く使うコマンドレット
ソート
ls | sort -Descending
ls | sort-object -Descending
ファインド
ls | Where-Object -FilterScript {$_.Name -like
"test*"}
これよりは、
ls | FindStr /I "test*"
のほうが楽。
エイリアス
Get-Command -CommandType Alias
スリープ
Sleep -m 500
# 500ミリ秒スリープ
Sleep -s 5
# 5秒スリープ
Sleep は、Start-Sleep コマンドレットのエイリアス
Com Object の利用
Word を使う
ワードのページ数を取得する方法
http://www.microsoft.com/japan/technet/scriptcenter/resources/qanda/sept06/hey0907.mspx
$w=new-object -com word.application
$w.visible = $true
$objDoc = $w.documents.open(".\test.doc")
$objDoc.ComputeStatistics(2)
$objDoc.Close()
$w.Quit()
Excel を使う
$e=new-object -com excel.application
$e.visible = $true
$oBook = $e.WorkBooks.OpenXML(".\test.xls")
$oBook.Close($false, , )
$e.Quit()
Remove-Variable oBookRemove-Variable e
# $oBook = $e.WorkBooks.Open("test.xls")
# だと、
# Exception calling "Add" with "0"
argument(s): "ライブラリの形式が古いか、または種類が無効です。
(HRESULT からの例外: 0x80028018
(TYPE_E_INVDATAREAD)) "
# というエラーになってしまう。
この原因は、ExcelがCultureを正しくハンドリングできないため。
http://support.microsoft.com/kb/320369/ja
PowerPoint を使う
$p=new-object -com
powerpoint.application
$p.visible = 1
# $true ではなく、1
$presSet = $p.Presentations
$pptPres = $p.Presentations.Open(".\test.ppt")$pptPres.Slides.Count
# ページ数
$pptPres.Close()
Remove-Variable pptPres
$p.Quit()
Remove-Variable p
インターネットエクスプローラーを使う
$ie = New-Object -ComObject
InternetExplorer.Application
$ie.Visible = $true
$ie.Navigate("http://www.microsoft.com/technet/scriptcenter/default.mspx")
# Navigateでは、非同期に処理が行われるので、待つ必要がある。
while($ie.Busy)
{
Sleep -m 500
}
$str = $ie.Document.Body.InnerHTML # ロードしたHTML
# $str にすべてのHTMLがStringで入ってしまう。
# $str=$ie.Document.Body.InnerHTML.Replace("<br>", "`n")
# $strArray=$str.split("`n") # というように、配列ばらすと、
# foreach ($line in $strArray) { ... 処理 ... } #
というように処理がしやすくなる。
$ie.Document.Body.InnerText # ロードしたText
終了処理
$ie.Document = $null
$ie.Quit
Remove-Variable ie
注意: 連続してNavigateしていく場合、$ie.Document =
$null をしないと、際限なくメモリを食っていく。
$ie.Document = $null をしておくと、一応物理メモリの上限程度で GCが働く模様。
ただし、Remove-Variable を行っても、ie のプロセスは生き残る。
また、Navigateで消費されたメモリも解放されない。IEで連続してページをロードしていくと、どんどんメモリを消費してしまう。したがって、$ie.Visible
= $false の場合は、IEのプロセスに注意する必要がある。
.NET Objectの利用
$o = New-Object -TypeName
System.Diagnostics.EventLog -ArgumentList Application
静的なクラスの参照
静的なクラスを参照するには、[]で囲む。
例 [System.Threading.Thread]
静的なクラスの利用
[System.Threading.Thread]::Sleep(10000)
静的なメソッド、プロパティであることは :: を指定する必要がある。
静的な列挙型
[System.Windows.Forms.WebBrowserReadyState]::complete
ただし、これを標準の状態で実行すると、
New-Object : 型 [System.Windows.Forms.WebBrowser]
が見つかりません。この型を含むアセンブリが読み込まれていることを確認してください。
発生場所 行:1 文字:11
+ New-Object <<<< -TypeName System.Windows.Forms.WebBrowser
というエラーになってしまう。
なぜかというと、
[System.AppDomain]::CurrentDomain.GetAssemblies()
で確認すると、System.Windows.Formsのアセンブリーが標準の状態でロードされていない。
そこで、
[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
により、System.Windows.Forms アセンブリーをロードする。
静的なクラスを調べる
[System.Environment] | Get-Member -Static
ロードされているアセンブリーを調べる
[System.AppDomain]::CurrentDomain.GetAssemblies()
アセンブリーのロード
[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
正規表現
-match は正確な正規表現でのマッチができない。
次の2つの処理で、を処理した場合。
$test=$test -match
"a(b|c)"
$test="ab, ac, bc"
$matches
Name Value
---- -----
1 b
0 ab
結果は、ab とb
一方、.NET Framework の 正規表現を使用すると・・・
$regex =[regex] "a(b|c)"
$test="ab, ac, bc"
$matches = $regex.matches($test)
$matches
結果は、ab と ac
Groups : {ab, b}
Success : True
Captures : {ab}
Index : 0
Length : 2
Value : ab
Groups : {ac, c}
Success : True
Captures : {ac}
Index : 4
Length : 2
Value : ac
どうも、-match は、Groupの値を間違って返すバグか?
正規表現の例
$regex =[regex] "&(?!amp;)"
# & 以外の&にマッチ
正規表現内の特殊文字のエスケープ
正規表現内の特殊文字 .$ ^
{ [ ( | ) * + ? \
これらは、バックスラッシュでエスケープする。
[a-z]のような範囲指定の中で使うには、-もエスケープする。
[a-z\-]