2010年11月30日火曜日

◆纏めてnslookup

ホスト名をテキストファイルにいれておき、nslookupを纏めて実行する。

001
002
003
004
005

$hosts = gc d:\desktop\hostname.txt 
"■■■以下のホストのアドレスを照会します■■■" | tee -Variable msg 
$hosts 
"■" * $msg.length 
$hosts | nslookup

結果。
image

◆ネットワークアダプターを無効にする

001
002
003
004
005

Get-WmiObject Win32_NetworkAdapter -Filter "NetConnectionID='Mylocal'" | %{ 
 
$retrn = $_.Disable().
ReturnValue 
 
if(!$return){ $_.NetConnectionID + " を無効にしました。" }else
 
  { 
$_.NetConnectionID +  " を無効にできませんでした。"} 
}

有効にする時はEnableメソッドで。
汎用的に作るならNetConnectionIDの存在チェックもしたほうが良いかも。

結果はこうなる。
20101130101727

2010年11月29日月曜日

◆PSCustomObjectは難しい?(Compare-Objectは難しい改)

PowerShell: ◆Compare-Objectは難しい?で思い通りの出力結果にならず思い悩んだが、S.K.さんからコメントを頂き、問題の本質が見えてきた。

001
002
003
004
005

$oldps = ps
notepad
 
$newps = 
ps
diff
 $oldps $newps 
diff $oldps $newps | gm

此の様に書いたとき、Get-Memberの出力が表示されず悩んでいたのだが、これはPowerShell: ◆コンソール出力のオブジェクトマージ機能を回避するで書いたのと同様の問題のようだ。
以下のようにOut-Hostを追加することで期待通りの結果が得られた。

001
002
003
004
005

$oldps = ps
notepad
 
$newps = 
ps
diff
 $oldps $newps 
diff $oldps $newps | gm | Out-Host

出力オブジェクトが同じ型の時にマージされるのが本当だと思うのだが、diffの結果として出力されるPSCustomObjectはそれ以降の出力オブジェクトをすべて巻き込んでしまう。
すなわち、PSCunsomObjet以降の出力はすべて同じ型であると判定されているようだ。

試しに同じPSCunsomObjetを出力するImport-CSVコマンドレットを使って以下のように書いてみた。

結果は先頭にCSVの内容を表示した後、空白行が続く。

001
002
003
004
005
006
007
008
009
010

#"★dir表示"
#dir | select -First 2
#"★ps表示"
#ps | select -First 2

"★★★CSV表示"
Import-Csv d:\Desktop\test.csv
"★dir表示"
dir | select -First 2
"★ps表示"
ps | select -First 2
20101129110823

コメントを外すと。
20101129111112

期待通りの結果になる。
先頭にPSCustomObjectが来るとダメっぽい。

これが仕様だとすると、嵌ること請け合いの仕様だ。

思い通りに出力されないときは「Out-Host」と覚えておこうっと。

2010年11月28日日曜日

◆文字コードを表示する

もう少しスマートな方法があるかもしれないが、とりあえずこんな感じで表示できた。
文字コードはUNICODE 。

001
002
003
004
005
006
007
008

function ConvertChar2Code($char){
$cArray=[char[]]$char 
$iArray = [int[]]
$cArray
$iArray
 = $iArray | %{[Convert]::ToString($_,16)}
0..$cArray.Count | %{Write-Host $cArray[$_] $iArray[$_]
}
}


. ConvertChar2Code("今日は犬と散歩に行きました")
結果。

今 4eca
日 65e5
は 306f
犬 72ac
と 3068
散 6563
歩 6b69
に 306b
行 884c
き 304d
ま 307e
し 3057
た 305f

<!--EndFragme

◆スコープ2

PowerShell: ◆スコープを書いた時には気付いていなかった方法があったので書いておく。

001
002
003
004
005
006
007
008
009
010
011
012

$a = 1
function fncA{
$a
$a
 += 1
}
function fncB{
$a
$a
 += 1
}
fncA
fncB
Write-Host
 $a

こんな風に書いたときC#などの感覚でいえば、$aはメンバー変数なので結果は1,2,3となっていくはずだ。
しかしPowershellの場合は以下のようになる。

20101128132910

これでは使いづらいので、functionにするのをためらいがちだった。
今日、偶然色々と試しているうちに良い方法を見つけた。
単にドットソースで呼んであげるだけで期待通りの結果になるのである。
(ドットソースは別スクリプトを取り込むものだとばかり思っていた・・・)

20101128133550

2010年11月27日土曜日

◆集計プロパティ(Select-Object)

以前PowerShell: ◆オブジェクトにプロパティを追加する(Add-Member)でも使用した集計プロパティ。

余り説明せずに使っていたので今一度その使い方を確認してみる。
集計プロパティはSelect-Objectだけで使えるものではないが、いちばん基本的なコマンドレットであるSelect-Objectを例に説明する。

PS>dir d:\desktop\test1


    ディレクトリ: D:\desktop\test1


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2010/11/25     15:28          0 test1.txt
-a---        2010/11/25     15:28          0 test2.txt


PS>dir d:\desktop\test1 | select mode,name

Mode                                                        Name
----                                                        ----
-a---                                                       test1.txt
-a---                                                       test2.txt

こんな感じでプロパティを射影するのが基本的な使い方。SQLと同じイメージ。

ここで出力フォーマットを弄ってみる。


PS>dir d:\desktop\test1 | select mode,{$_.name + "です"}

Mode                                                        $_.name + "です"
----                                                        ----------------
-a---                                                       test1.txtです
-a---                                                       test2.txtです


本来プロパティ名を指定するところに式を指定するのでスクリプトブロックで指定する。
スクリプトブロックの中ではパイプラインオブジェクトが$_変数で参照できる。
ただし、これではタイトルがいただけない。
この様な時のためにタイトルを指定する機能を提供するのが集計プロパティと呼ばれるものである。


PS>dir d:\desktop\test1 | select mode,@{name="ファイル名は";expression={$_.name + "です"}}

Mode                                                        ファイル名は
----                                                        ------------
-a---                                                       test1.txtです
-a---                                                       test2.txtです

もともとあったスクリプトブロックにexpressionというキーを付け、加えてnameというキーでタイトルを追加している。キーと値のセットなので指定形式は@{}がついた連想配列形式になる。2個のキーと値の設定を1行で書いているので間にセミコロンを入れている。コマンドラインからではなくスクリプトファイルとして書くのならば以下のように書くのも良いかも。

001
002
003
004
005

$fileprop = @{
name
="ファイル名は"
expression={$_.name + "です"
}
}

dir d:\desktop\test1 | select mode,$fileprop

ちなみに、上記のハッシュテーブルは以下のように書いても良い。

001
002
003

$fileprop2 = @{}
$fileprop2.name="ファイル名は"
$fileprop2.expression={$_.name + "です"}

と、思っていたのだが・・・。
実際以下のようにどうみても同じに見える。
20101128003238

しかし、$fileprop2のほうは以下のようなエラーになる。

Select-Object : System.Management.Automation.PSObject を型 {System.String, Syst
em.Management.Automation.ScriptBlock} のいずれかに変換することはできません。
発生場所 D:\temp\b4b9f59f-3638-4969-9a96-d4d07f915788.ps1:16 文字:30
+ dir d:\desktop\test1 | select <<<<  mode,$fileprop2
    + CategoryInfo          : InvalidArgument: (:) [Select-Object]、NotSupporte
    dException
    + FullyQualifiedErrorId : DictionaryKeyUnknownType,Microsoft.PowerShell.Co
   mmands.SelectObjectCommand

だいぶ悩んで$fileprop1と$fileprop2の違いを調べたがどうにもわからない。
個人的にはバグ(bug)にしか思えないのだが・・・・。
とりあえず1の書き方をすればよいだけなので放置。

最後にSelect-Objectの使い方(オプション)を。集計プロパティを使っているが、コメントにしてあるAdd-Memberのほうを使ったも結果は同じになる。

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017

$ps = Get-Process
$script:i = 0
#$ps | %{$i=0}{
#Add-Member -MemberType NoteProperty -Name No -Value (++$i) -InputObject $_
#}

$no = @{name="No";expression={(++$script:i)}}
$ps = $ps | select * ,$no
"先頭から3件"
$ps | select No,processname -First 3 | ft -AutoSize
"最後から3件"
$ps | select No,processname -Last 3 | ft -AutoSize
"0番目と12番目"
$ps | select No,processname -index 0,12
"0番目と12番目"
$ps | select -index 0,12
"modulesを展開"
$ps | select $name -ExpandProperty modules -First 1
</

結果は以下の通りなのだが、またしても色々と嵌った。(嵌ってばかりだ・・・)
まず、Indexパラメータ。なぜかエラーになる・・・。
どうやらPropertyパラメータと一緒に使えないらしい。
それじゃSelectじゃないと思うのだが・・・。

プロパティが連想配列の時(だけかは?)にExpandPropertyを指定すると中身を展開してくれる。これもPropertyとは併用できないようだ。(こちらはエラーにはならずに無視される)

今日もやけに疲れた・・・。

先頭から3件

No ProcessName
-- -----------
1 BaiduJPEngine
2 BWH32S
3 cmvMain


最後から3件

No ProcessName
-- -----------
65 WLIDSVC
66 WLIDSVCM
67 wmpnetwk


0番目と12番目
Select-Object : 引数 'System.Object[]' を受け入れる位置指定パラメーターが見つかりません。
発生場所 行:9 文字:13
+ $ps | select <<<<  No,processname -index 0,12
    + CategoryInfo          : InvalidArgument: (:) [Select-Object]、ParameterBindingException
    + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.Selec

0番目と12番目

Handles NPM(K)  PM(K)  WS(K) VM(M) CPU(s)   Id ProcessName
------- ------  -----  ----- ----- ------   -- -----------
     77      4  12884  69044   136  97.06 3180 BaiduJPEngine
    513     37 114288 130776   310 273.75 2936 firefox
modulesを展開

Name              : BaiduJPEngine
ModuleName        : BaiduJPEngine.exe
FileName          : C:\Program Files\Baidu\Type\BaiduJPEngine.exe
BaseAddress       : 4194304
ModuleMemorySize  : 86016
EntryPointAddress : 4201966
FileVersionInfo   : File:             C:\Program Files\Baidu\Type\BaiduJPEngine.exe
                    InternalName:     BaiduJPEngine
                    OriginalFilename: BaiduJPEngine.exe
                    FileVersion:      1, 0, 0, 20
                    FileDescription:  BaiduJP IME Engine
                    Product:          BaiduJP IME Engine
                    ProductVersion:   1, 0, 0, 20
                    Debug:            False
                    Patched:          False
                    PreRelease:       False
                    PrivateBuild:     False
                    SpecialBuild:     False
                    Language:         日本語 (日本)

Site              :
Container         :
Size              : 84
Company           : Baidu Inc.
FileVersion       : 1, 0, 0, 20
ProductVersion    : 1, 0, 0, 20
Description       : BaiduJP IME Engine
Product           : BaiduJP IME Engine

◆ショートカットを作る

WSHで定番のショートカット作成。

001
002
003
004
005
006
007

$wsh = New-Object -comObject WScript.Shell
$link = $wsh.CreateShortcut("D:\desktop\notepad.lnk")
$link.TargetPath = "notepad.exe"
$link.Description = "メモ帳"
$link.WorkingDirectory = 
$home
$link
.IconLocation = "notepad.exe"
$link.Save()

2010年11月25日木曜日

◆Compare-Objectは難しい?

Compare-Object、エイリアスはCompare

ちなみに、これまで使った限りでは、XXXX-ObjectのエイリアスはXXXXだ。
New-Objectだけ(かどうかは調べていないが)はなぜかNewではだめ。
Compare-Objectにはdiffというエイリアスもあるのでそちらのほうが通りがよいかもしれない。

で、本題だが、Compare-Objectを使って以下のありがちなコードを書いてみた。

001
002
003
004
005

$oldps = ps
notepad
 
$newps = 
ps
diff
 $oldps $newps | 
gm
diff
 $oldps $newps
 

結果は
 20101125212044

まぁ、予想通りの結果だ。
説明するまでもないかもしれないが(私はよく判っていなかった)、「=>」の意味するところは、InputObjectに表示されたものが、diffの引数の右側(第2引数)にしか無いよという事。

問題なのは、このスクリプトの4行目と5行目を単純に入れ替えた時だ。
すると、結果はこうなる。
20101125214656

@@ Get-Memberはどこへ? 
 
Tee-Objectなんかしても何やらおかしい。
20101125233628

4行目のコメントをはずすと、

20101125233918

GetTypeの表示の他に6行目のGet-Memberが有効になる。

果たして、BUGなのか私の知らない仕様が存在するのか・・・・。

そもそもCompare-Objectのヘルプによると、このコマンドレットのパイプライン出力は無いと書いてある。
なので、出力されるのがたまたま(BUG?)で出力されないのが正しいのかもしれない。

というわけで、Cpmpare-Objectのパイプライン出力には触れないのが得策の様だ。
Cpmpare-Objectにはパイプライン出力用のpassthruスイッチが用意されているのでそちらを使うのがまっとうな使い方なのだろう。
20101125235734

比較して相違のあったオブジェクト(ここではメモ帳のプロセスオブジェクト)がやって くる。
ただし、ここでもTee-Objectはダメっぽい。というか私が何か理解不足なのか。

20101126000317

個々のプロパティが一つのクラスとして表示されている。
ただ、そのどれもがProcessクラスというのはどう考えてもおかしいような・・・。

ん~、Teeも使わないことにする。

色々と紆余曲折し悩んだが、最後にまっとうなCompare-Objectのサンプルを。
上記のPassthruでやってくるProcessオブジェクト、型としてはProcessなのだが、Powershellが独自にプロパティを追加しているようだ。
以下でそれを表示してみる。

001
002
003
004
005
006
007
008
009
010

$oldps = ps
notepad
 
$newps = 
ps
diff
 $oldps $newps -PassThru | Set-Variable result
$result | gm | Set-Variable psprocess

ps | gm | Set-Variable normalprocess

diff $psprocess $normalprocess -Property name,membertype 
$result.Kill(
)

結果は以下の通り。

20101126004021

例の差異を表す「<=」がNotePropertyとして追加されているのが判る。

あぁー、なんか疲れた。

◆コンソール出力のオブジェクトマージ機能を回避する

次のような何の変哲もないコマンド。
20101125161945

さて、これを2行続けた以下のようなスクリプトを書くとどうなるか。
普通は全く同じ結果を予想すると思うが、実際にやってみると若干違った結果が返ってくる。(ISEで実行)
20101125163140 

結果がマージされて出力されいる。
意外と普段気が付かないがPowershellは出力バッファ内の同じタイプのものはマージして見やすい出力結果に変えてくれている。

通常はこの方が見やすい事は確かだと思うのだが、自分で出力結果を整形したい場合などには若干困ったことになる。
20101125165058 

その場合は以下のようにひと手間掛けてあげるとよい。
20101125165300

2010年11月24日水曜日

◆変数の値を限定する

 

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023

#$arg = ""
function funcArgCheck
{
   
param($arg
)
   
trap
{
       
"引数の範囲は"
        (get-variable vari -Scope script).Attributes |
         %{if($_ -is [Management.Automation.ValidateSetAttribute]
){
        
$_ | select -ExpandProperty validvalues;"です";break
} }
    }
   
$script:vari = "one"
    Get-LimitedArg "One","Two","Three"
    $script:vari = $arg
    Write-host $arg "OK`n"
}

function Get-LimitedArg($values
){
   
$opt = New-Object Management.Automation.ValidateSetAttribute
 `
     
-ArgumentList $values
    (Get-variable vari -Scope script).Attributes.Add($opt
)
}


funcArgCheck "Two"
funcArgCheck 
"Five"

ValidateSetAttributeを使って変数の値範囲を制限してみた。

本質的な部分はGet-LimitedArg関数の部分だけなので簡単に実装できる。

関数分けしたりトラップしたりがベタなコードになってしまったが、とりあえずこんなこともできるよという事で。

結果は、
20101124211630

◆URLエンコード・デコードする

 
001
002
003
004
005

$esc = "[System.Uri]::EscapeDataString"
[System.Uri]::EscapeDataString("Powershellのブログ") | tee -Variable e1
[System.Uri]::EscapeDataString("<h1>Hello World</h1>") | tee -Variable e2
[System.Uri]::UnescapeDataString($e1)
[System.Uri]::UnescapeDataString($e2)

結果

20101124000853

2010年11月23日火曜日

◆Trueの判定

PowershellでのIF文のTrue判定は .NET とは若干違っている。

PowerShell: ◆IPアドレスを検証するにて
if($object) のような判定を行っているが、.NETであればこの場合$objectはbool型である。

しかし、Powershellの場合$objectの型は何でもOKだ。
基本的には、その型の初期値であればFalse、それ以外であればTrueと覚えておくと覚えIやすい。(厳密には若干不正確な説明かもしれないが)

以下に例を示す。

001
002
003
004
005
006
007
008
009
010
011
012
013

function IsTrue($para){
  $result = "◆Param Type:({0}) Value:({1}) IF:({2})"
  $TorF = if($para){"True"}else{"False"}
  $type = if($para -eq $null
){
   
"NullType"}else{$para.GetType().Name}
  $result -f $type , $para , $TorF
}
IsTrue("")
IsTrue("test")
IsTrue(0)
IsTrue(5)
IsTrue(New-Object -ComObject "shell.application")
IsTrue($null)

結果は

20101123210706

-- 追記(20145.1.5) --

PowerShell: ◆IF文での判定条件の書き方

◆IPアドレスを検証する

有効なIPアドレスを検証する。
正規表現などを使って検証する例も見かけるが、こんな方法もある。
-as演算子で型変換すると変換できない場合はエラーにならずにNullになる。.NETと同じだ。
なので変換してみてOKなら正しいと判断する。

また、if($object)という使い方、ここではさしあたってNulの時にFalseになる。
(厳密にいうと以下の使い方には若干バグがある)

001
002
003
004
005
006
007
008
009
010
011
012

function isIPAddress($object) {
if($object){$object}else{"Null"
}
  (
$object -as [System.Net.IPAddress]).IPAddressToString -eq $objectand
 
  (
$object -ne $null)
  Write-Host
}

isIPAddress "10.256.0.1"
isIPAddress "127.0.0.1"
IsIPAddress "hello"
IsIPAddress "192.168.0.1.1"
isIPAddress $null

結果はこんな感じになる。
20101123170544

メールアドレスの検証も同様に、こんな感じで。
[System.Net.Mail.MailAddress]).Address

2010年11月22日月曜日

◆共有フォルダーの設定・解除

共有フォルダーの設定と解除を行う。

001
002
003
004
005
006
007
"--- Tools フォルダを共有設定する ---"
([WMICLASS]"WIN32_Share").Create("c:\TOOLS","tools",0) | Out-Null
net share

"--- Tools フォルダを共有解除する ---"
Get-WmiObject -Class "Win32_Share" -Filter "Name='tools'" | Remove-WmiObject | Out-Null
net share

結果は以下の通り。

image

◆特殊フォルダーの取得

EnvironmentクラスのGetFolderPathメソッドを使うと取得できる。

PS>[environment]::GetFolderPath("desktop")
D:\Desktop
PS>[environment+specialfolder] | Get-Member -MemberType property -Static


   TypeName: System.Environment+SpecialFolder

Name                  MemberType Definition
----                  ---------- ----------
ApplicationData       Property   static System.Environment+SpecialFolder Ap
CommonApplicationData Property   static System.Environment+SpecialFolder Co
CommonProgramFiles    Property   static System.Environment+SpecialFolder Co
Cookies               Property   static System.Environment+SpecialFolder Co
Desktop               Property   static System.Environment+SpecialFolder De
DesktopDirectory      Property   static System.Environment+SpecialFolder De
Favorites             Property   static System.Environment+SpecialFolder Fa
History               Property   static System.Environment+SpecialFolder Hi
InternetCache         Property   static System.Environment+SpecialFolder In
LocalApplicationData  Property   static System.Environment+SpecialFolder Lo
MyComputer            Property   static System.Environment+SpecialFolder My
MyDocuments           Property   static System.Environment+SpecialFolder My
MyMusic               Property   static System.Environment+SpecialFolder My
MyPictures            Property   static System.Environment+SpecialFolder My
Personal              Property   static System.Environment+SpecialFolder Pe
ProgramFiles          Property   static System.Environment+SpecialFolder Pr
Programs              Property   static System.Environment+SpecialFolder Pr
Recent                Property   static System.Environment+SpecialFolder Re
SendTo                Property   static System.Environment+SpecialFolder Se
StartMenu             Property   static System.Environment+SpecialFolder St
Startup               Property   static System.Environment+SpecialFolder St
System                Property   static System.Environment+SpecialFolder Sy
Templates             Property   static System.Environment+SpecialFolder Te

2010年11月21日日曜日

◆コンピュータにしゃべらせる

これは面白い。使い道ありそう。

001
002
$sam = New-Object -comObject SAPI.SpVoice
$sam.Speak("My name is ($env:username)" )

◆PowerGUIを使う

PowershllISEが出る前に使っていたのだがISEが出てからは使っていなかった。
最近また使い始めたらだいぶパワーアップして実に便利なので紹介しておく。

インストールは何も難しいことはない。
使い勝手としてはPowershellにVisualStudioのインテリセンスがやってきた、という感じであろうか。どうしても起動はちょっと重いが、起動してしまえば快適そのものである。
インストールすると以下の2つのアイコンが登録される。
image
通常はScriptEditorのほうを使ってスクリプトを書いていくことになる。もう一つのほうがはVisualStudioでいうところのソリューションエクスプローラーみたいなものかと思っているが、私は使っていないので詳しくはわからない。(追記:システム管理ツールですね)

コマンドレットを打ち込んでいくと以下のような感じで候補を表示してくれる。
image

シンタックスも表示してくれるのは実にうれしい。まさにインテリセンス並み。
パラメーターもちゃんと表示する。
image

コードスニペット挿入の機能もある。

スクリプトを実行すると変数ウインドウに自動変数なども含めて現在のスコープの変数が表示される。これでを参照すればデバッグ効率は格段に向上する。
image

あと、私が気に入っているのがこのボタン。
image

これは現在のスクリプトを新しいセッションで外部ウインドウとして実行してくれる。ISEを使っていたときは、通常のスクリプトとして実行した時とISEで実行した時の結果が違うことがあるので、いちいち両方でテストするのが面倒だったが、これがあるおかげでクリーンな状態で簡単にテストができる。

私はWindowsLiveWriterを使ってブログを書いているのだが、なにより嬉しかったのがアドオンでWindowsLiveWriterにソースを張り付ける機能が提供されていたこと。
WindowsLiveWriter自体にもソースコード挿入用のアドインはあるのだが、当然Powershell用のシンタックスは用意されていないため色分けとかが効かない。

PowerGUIのアドオンの追加は若干面倒なので(結構悩んだ)ちょっと説明しておく。
image
アドオン用のコミュニティサイトが開くので好きなものを選んでダウンロードする。
今回は試しに以下のBlueConsoleを選んでみた。
解凍して出てきたファイルを以下のフォルダーにコピー。(必ずしもここじゃなくともよいのだとは思うが、ここにしろと書いてあったので)
D:\Documents\WindowsPowerShell
次に以下のメニューを開く。
image

Add Moduleを開く。
image

ここで解凍したフォルダーを選べばOK。

っと説明にはあるのだが、昨日色々試行錯誤していたらいつの間にかうまくいったのだが今日やるとうまくいかない。

そこで、元から存在していたBitsモジュールの場所を探してそこに置いてみたらビンゴ。
C:\Windows\System32\WindowsPowerShell\v1.0\Modules
image

もう一度PowerwGUIに戻ってライブラリーメニューを開くと
image

Add-on.BlueCOnsoleが表示されているのでチェックをつけてOK.
エディター画面に戻ると以下のようにコンソール出力がBlueになっている。
image

会社の環境で試したら、今度は最初の手順でOKだった。
よくわからないが、まぁどちらかの方法で追加できることは確実なようだ。