2010/02/22

PowerShell 2.0では標準コマンドレットの数が1.0の129個から236個へと、107個増えています。また、既存のコマンドレットの一部にパラメータが増えています。そこで、2.0で新しく加わったコマンドレットと、新しく追加されたパラメータを列挙するスクリプトを作ってみました。

まず、1.0がインストールされている環境で、コマンドレットのリストをXMLに書き出します。次のコマンドを実行してください。

 get-command -type cmdlet|export-clixml cmdlets1.xml -depth 3

同様に、2.0の環境でも実行してください。

 get-command -type cmdlet|export-clixml cmdlets2.xml -depth 3

出来上がった二つのxmlファイルをカレントディレクトリに置いて、次のスクリプトを実行すると、新しく追加されたコマンドレットとパラメータが列挙されます。(必要なら適宜リダイレクトするなどしてファイルに落とし込んでください)

function Get-CmdletHash
{
    param([string]$path)
    $cmdlets = Import-Clixml $path
    $cmdletsHash = @{}
    $cmdlets|
        %{
            $parameters = @()
            $_.ParameterSets|
                %{
                    $_.Parameters|
                        %{
                            $parameters += $_.Name
                        }
                }
            $parameters = $parameters|Sort-Object|Get-Unique|?{"WarningAction","WarningVariable" -notcontains $_}
            $cmdletsHash.Add($_.Name,$parameters)
        }
    return $cmdletsHash
}

$ver1 = Get-CmdletHash cmdlets1.xml
$ver2 = Get-CmdletHash cmdlets2.xml

$ver2.Keys|
    %{
        if($ver1.ContainsKey($_))
        {
            $result = Compare-Object $ver1[$_] $ver2[$_]
            if($result)
            {
                "[Update] $_"  
                $result | Format-Table
            }
        }
        else
        {
            "[New] $_  `r`n" 
        }
    }

出力結果を置いておきます。こちら

このスクリプトではGet-CommandコマンドレットがSystem.Management.Automation.CmdletInfoというコマンドレットの情報を格納したオブジェクトを返すことを利用し、Export-Clixmlコマンドレットでシリアライズ化してXMLファイルに落とし込むことにより異なるバージョンのPowerShell同士を比較しています。ここで-depthパラメータを3にしているのは、パラメータ情報を格納するParametersプロパティがルートから3階層下にあるためです。(デフォルトは2階層下までを出力。PS2.0ではCmdletInfoにParametersプロパティというのがあって2階層でたどれるのですが1.0にこのプロパティはないのでParameterSetsプロパティからたどる必要があります)

また、WarningActionとWarningVariableという共通パラメータが増えているので、これらはすべてのコマンドレットに共通して追加されているパラメータなので除外しておきます。

あとはパラメータをコマンドレットごとに配列化して、Compare-Objectで比較しています。新しく追加されたコマンドレットは[New]のマークをつけ、既存のコマンドレットでパラメータに変化があるものは[Update]をつけて追加されたパラメータを列挙するようにしています。

かなりたくさんのコマンドレットでパラメータが増えたことが分かりますね。また、Get-CommandコマンドレットのPSSnapinパラメータが廃止され、Moduleパラメータに変更されたことなどが分かったりします。

1.0ユーザーだった方にお勧めのスクリプトです。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2010/02/22/186334.aspx

2009/09/23

こんにちは、むたぐちです。Windows 7とWindows Server 2008 R2を使っていろいろと遊ん…研究しています。

Win7はVistaのマイナーチェンジ版とよく言われますが、カーネルからかなりのチューニングが入っており、またシェルもかなり使い勝手が良くなっていて、単にVistaは重いからWin7は軽くしてみました、以上のものがあります。

今回はWin7で加わった新しい機能である、ライブラリについて注意点を書いてみます。

ライブラリは、エクスプローラを開くとデフォルトで表示される、仮想ディレクトリです。デフォルトでは、ミュージック、ビデオ、ピクチャ、ドキュメントの4つがあります。それぞれ、対応する実フォルダと関連付けられていて、複数のフォルダをまるで一つのフォルダのように扱えます。もちろんフォルダを追加することも、ライブラリ自体を新しく作ることもできます。ライブラリはデフォルトで挙げられたものと汎用のものの計5タイプがあり、それぞれ少しずつ機能が違います。たとえば、ミュージックタイプはアーティストごとにグループ化できたりします。

さて、ローカルフォルダをライブラリに追加するのは特に何の問題もありませんが、リモートフォルダをライブラリに追加するにはいくつかの要件があります。

ライブラリは、Windows Search サービスが作成するインデックスをもとに構築されます。そのため、リモートフォルダはインデックスが構築されている必要があります。Windows Search サービスが動作しているOSはWindows Vista / Server 2008 以上です。なので、残念ながらXPや2003 Serverなどのリモートフォルダはライブラリに追加できません。2009/10/11訂正。Windows Search 4.0を入れれば可能です。渋木宏明(ひどり) さんありがとうございます。

注:ただし、Windows Media Player 12を使った場合、グループ化などはできないものの、未対応のフォルダでも強引にライブラリに追加できる。ちなみにWMP12のライブラリはエクスプローラのライブラリと同一のものです。

さて、Windows Vista/7ではWindows Search サービスはデフォルトで稼働していますが、Server 2008 /Server 2008  R2 ではデフォルトオフです。なので、これを有効化してやる必要があります。その方法は、役割の追加で「ファイルサービス」を追加し、役割サービスの追加で「Windows Search サービス」を追加します。(この場合、「Windows Server 2003ファイルサービス」は使用不可能となる)

これで、一応リモートサーバーにあるフォルダをライブラリに追加できますが、このままではミュージックライブラリでグループ化ができません。なぜかというと、Server 2008にはWMP11 or 12がデフォルトでは有効化されていないため、インデックスサービスがmp3ファイルなどのアーティスト名等のプロパティ(ID3タグ)を参照できないためです。

WMPを有効化するには、機能の追加で、「デスクトップ エクスペリエンス」を追加します。

これで、晴れて2008/R2にあるフォルダを完全な形でライブラリに追加できます。

ライブラリは非常に面白い機能なのでぜひ使ってみてください。過去の思わぬファイルなんかがみつかったりしますよ。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2009/09/23/181554.aspx

2007/12/16

Get-ChildItemはパラメータのディレクトリ配下にあるFileInfoオブジェクトとDirectoryInfoの配列を返すのでそのLengthプロパティをとれば個数が求まります。たしかにそうなんですけど、hogehogeにファイルがただ一つある場合、配列ではなく一つのFileInfoオブジェクトが返されます。なので、そのLengthプロパティ(バイト数)を取ってきてしまうのです。せめてエラーになればいいんですが、ArrayとFileInfoで同じLengthプロパティを持つための悲劇です。

これを防ぐには、@(Get-ChildItem hogehoge).Lengthとします。ファイルが一個の場合でも強制的に配列にしますので戻り値は1となります。

これではまったー。フォルダ配下のすべてのフォルダにあるファイル数をそれぞれ列挙するスクリプトで@をつけなかったばかりに・・・

PS D:\script> dir -rec |%{if((Get-Item $_.fullname).PSISContainer){[string]@(dir $_.fullname).length + "   " + $_.Fullname}}|more

[string]にキャストしてる理由は、PowerShellが演算子の左の型に右の型をキャストするためです。キャストしないと"    "という文字列をintに変換しようとしてエラーになります。

こういうときに連想配列使いたいですが連想配列って値でソートできないんですよねぇ。(まぁテクはありますが)

元記事:http://blogs.wankuma.com/mutaguchi/archive/2007/12/16/113134.aspx

2007/07/21

Get-Helpコマンドレットに-fullオプションを付けると、コマンドレットのパラメータの説明に「必須」、「位置」、「既定値」、「パイプライン入力を許可する」、「ワイルドカード文字を許可する」という項目が追加されます。この中で「パイプライン入力を許可する」がtrueになっている場合は、パイプラインからの入力がそのパラメータに渡されるという意味なのですが、これにはByValueとByPropertyNameの二種類があります(同時に指定されていることも)。

この意味お分かりになられますか?

mixiコミュでいろいろと議論した結果、ようやく分かったのでここでご報告しておきます。

ByValueはオブジェクトがそのまま渡ります。これは特に問題ないでしょう。

ByPropertyNameは、パイプを渡ってきたオブジェクトのプロパティが、パラメータ名と一致した場合、そのプロパティをパラメータとして解釈するという意味です。

具体的にGet-ChildItemコマンドレットを取り上げましょう。

Get-ChildItemコマンドレットは-pathパラメータがtrue (ByValue, ByPropertyName)、-literalPathパラメータ(エイリアスは-PSPath。ちなみにパラメータのエイリアスを調べるには(Get-Command Get-ChildItem).parametersetsのようにするとパラメータの一覧が出ますので、そのAliasを見てください)がtrue (ByPropertyName)です。

よって、入力オブジェクトにPathプロパティがあればその値が-pathパラメータに渡ります。(なければ入力オブジェクトがそのまま-pathパラメータに渡されます)。また、入力オブジェクトにLiteralPathプロパティまたはPSPathプロパティがあれば、-literalPathパラメータにその値が渡ります。これを検証します。

Get-ChildItemコマンドレットの戻り値はファイルシステムプロバイダにおいてはFileInfoオブジェクトとDirectoryInfoオブジェクトを含んだ配列です。これらのオブジェクトにはPSPathプロパティがあるので、この結果をパイプで次のGet-ChildItemコマンドレットに渡すと、そのPSPathプロパティが、-literalPathパラメータに渡ります。すなわちこういうことです。

PS C:\script> Get-ChildItem a*|Get-ChildItem


    ディレクトリ: Microsoft.PowerShell.Core\FileSystem::C:\script


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2007/07/20     18:33          0 a.txt
-a---        2007/07/21     16:17       8826 about_Alias.help.txt

このコマンドに意味があるかどうかは別にして、そういうことが可能だということです。

もっと分かりやすいと思われる例を示しましょう。$aというオブジェクトを作成し、それにAdd-MemberコマンドレットでPathという名前のNotePropertyを追加します。そして$aをパイプラインを通じてGet-ChildItemコマンドレットに渡すとどうなるかご覧ください。

PS C:\script> $a = New-Object PSObject
PS C:\script> $a = $a | Add-Member noteproperty Path "a*" -passthru
PS C:\script> $a.path
a*
PS C:\script> $a|Get-ChildItem

    ディレクトリ: Microsoft.PowerShell.Core\FileSystem::C:\script

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2007/07/20     18:33          0 a.txt
-a---        2007/07/21     16:17       8826 about_Alias.help.txt

というわけで無事、Pathプロパティが-pathパラメータに渡っていることがお分かりいただけると思います。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2007/07/21/86361.aspx

2006/12/08

前のブログを閉鎖するので、これから元記事を一気にコピーします。

元記事はこちらですhttp://winscript.s41.xrea.com/mt/archives/2005/09/post_4.html

.NET FrameworkのSystem.Windows.Formsに含まれるコントロールを利用して、簡易ファイラをつくってみました。テキストボックスにパスを入れて「移動」ボタンを押すとそのフォルダの中身をリストボックスに表示します。リストボックスのファイルをダブルクリックすると実行、フォルダをダブルクリックすると移動します。イベントハンドラの使い方に注目してください。

function InvokeItem {
    Param($path)
    
    # 現在のパス+選択したファイル/フォルダ名を組み立てる
    $path2 = $(get-location).ToString() +"\" + $path
    
    if ([System.IO.Directory]::Exists($path2) ){
    # フォルダなら移動
        ChDirectory($path2)
    }else{
    #ファイルなら実行
        invoke-item $path
    }
}
 
function ChDirectory {
    Param($path)
    
    set-location $path #ディレクトリ移動
    $listBox1.Items.Clear() #リストボックスを空にする
    
    # get-childitem Cmdletの戻り値をパイプラインに渡し、
    # foreachしてリストボックスに追加する
    get-childitem | foreach{$r = $listBox1.Items.Add($_)}
}
 
 
[void] [Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 
[void] [System.Windows.Forms.Application]::EnableVisualStyles()
$form = new-object System.Windows.Forms.Form
$form.Size = new-object System.Drawing.Size(300, 400)
 
$textbox1 = new-object System.Windows.Forms.TextBox
$textbox1.Size = new-object System.Drawing.Size(250, 20)
$textbox1.Location = new-object System.Drawing.Point(0, 0)
$textbox1.Text = get-location
 
$button1 = new-object System.Windows.Forms.Button
$button1.Size = new-object System.Drawing.Size(40, 20)
$button1.Location = new-object System.Drawing.Point(250, 0)
$button1.Text = "移動"
# ButtonのClickイベント
$button1.Add_Click({ChDirectory($textbox1.Text)})
 
$listbox1 = new-object System.Windows.Forms.ListBox
$listBox1.Size = new-object System.Drawing.Size(250, 300)
$listBox1.Location = new-object System.Drawing.Point(0, 50)
# ListBoxのDoubleClickイベント
$listBox1.Add_DoubleClick({InvokeItem($listBox1.SelectedItem)})
 
# 初期ディレクトリの設定
ChDirectory($textbox1.Text)
 
# コントロールをフォームに配置して表示
$form.Controls.Add($textbox1)
$form.Controls.Add($button1)
$form.Controls.Add($listBox1)
$form.showDialog()
元記事:http://blogs.wankuma.com/mutaguchi/archive/2006/12/09/49648.aspx

2006/11/07

cmd.exeでdir /bとすると、カレントにあるファイル名・ディレクトリ名だけを出力します。以下、例。

C:\WINDOWS\system32\windowspowershell\v1.0>dir /b
certificate.format.ps1xml
dotnettypes.format.ps1xml
examples
filesystem.format.ps1xml
help.format.ps1xml
ja
powershell.exe
powershellcore.format.ps1xml
powershelltrace.format.ps1xml
pwrshmsg.dll
pwrshsip.dll
registry.format.ps1xml
types.ps1xml

同じことをPowerShellにやらせるにはどうすればいいか、考えてみました。

【案1】

PS C:\WINDOWS\system32\WindowsPowerShell\v1.0> ls|format-wide -c 1

    ディレクトリ: Microsoft.PowerShell.Core\FileSystem::C:\WINDOWS\system32\Win
    dowsPowerShell\v1.0

[examples]
[ja]
certificate.format.ps1xml
dotnettypes.format.ps1xml
filesystem.format.ps1xml
help.format.ps1xml
powershell.exe
powershellcore.format.ps1xml
powershelltrace.format.ps1xml
pwrshmsg.dll
pwrshsip.dll
registry.format.ps1xml
types.ps1xml

Format-Wideコマンドレットを使った例。うーんちょっと違う。でもディレクトリに[]が付くのでわかりやすいかも。これはこれで。

【案2】

PS C:\WINDOWS\system32\WindowsPowerShell\v1.0> ls|select name
Name
----
examples
ja
certificate.format.ps1xml
dotnettypes.format.ps1xml
filesystem.format.ps1xml
help.format.ps1xml
powershell.exe
powershellcore.format.ps1xml
powershelltrace.format.ps1xml
pwrshmsg.dll
pwrshsip.dll
registry.format.ps1xml
types.ps1xml

Select-Objectコマンドレットを使ってNameプロパティだけをもったPSCustomObjectのArrayを作っているのでこんな感じに出力されます。
Name
----
が邪魔ですね。

【案3】

PS C:\WINDOWS\system32\WindowsPowerShell\v1.0> ls|%{$_.name}
examples
ja
certificate.format.ps1xml
dotnettypes.format.ps1xml
filesystem.format.ps1xml
help.format.ps1xml
powershell.exe
powershellcore.format.ps1xml
powershelltrace.format.ps1xml
pwrshmsg.dll
pwrshsip.dll
registry.format.ps1xml
types.ps1xml

これでdir /bと同じ結果になりました。Foreach-ObjectでNameプロパティだけを取り出して出力しているわけです。

【案4】

PS C:\WINDOWS\system32\WindowsPowerShell\v1.0> ls|split-path -leaf
examples
ja
certificate.format.ps1xml
dotnettypes.format.ps1xml
filesystem.format.ps1xml
help.format.ps1xml
powershell.exe
powershellcore.format.ps1xml
powershelltrace.format.ps1xml
pwrshmsg.dll
pwrshsip.dll
registry.format.ps1xml
types.ps1xml

Split-Pathコマンドレットを-leafオプション付きで使っても同様の出力が得られました。これ実は何故こうなるのかよくわからないんですけど、たぶんパイプを渡るときに、System.IO.DirectoryInfoオブジェクトやSystem.IO.FileInfoオブジェクトのNameプロパティが渡されてるか、ToString()メソッドが実行されているのでしょうね。ps1xmlファイルの何らかの記述でこうなっているのかもしれません。でもまあ仕組みが分からなくてもこの動作は非常に合理的であります。

【案5】

PS C:\WINDOWS\system32\WindowsPowerShell\v1.0> ls -name
examples
ja
certificate.format.ps1xml
dotnettypes.format.ps1xml
filesystem.format.ps1xml
help.format.ps1xml
powershell.exe
powershellcore.format.ps1xml
powershelltrace.format.ps1xml
pwrshmsg.dll
pwrshsip.dll
registry.format.ps1xml
types.ps1xml

ていうかこんなことをしなくても、Get-ChildItemコマンドレットには-nameオプションがあり、dir /bと同じ効果が得られるのでした。ヘルプの見落としでこんな回りくどいことを考えていたのです。すみません、こんなオチで。でもまあ同じことをやるのに色んな手段があるということが分かったので良しとしましょう。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2006/11/07/43902.aspx

2006/11/01

PowerShellのコマンドレットのヘルプを引くには、get-help コマンド名 -detailなどとしますが、コマンドラインでいちいち打つのは邪魔くさい、一覧のヘルプファイルが欲しい、という場合に便利なワンライナーを書いてみました。

カレントにファイルが作成されますので、適宜cdで移動してください。

コマンドレットのヘルプのHTMLを作成するワンライナー

get-command -commandtype cmdlet|%{"

" + (get-help $_.Name -detail|out-string).Replace("&","&").Replace("<","<").Replace(">",">").Replace("`n","
") +"

"|out-file($_.name + ".html")}

HTMLのインデックスを作成するワンライナー

$temp="";out-file index.html -inputobject $temp

これを実行すると、カレントディレクトリに、コマンドレットのヘルプが記述された「コマンドレット名.html」というファイルが、登録されている数だけ作成され、インデックスとなるファイルが「index.html」として作成されます。

あとはindex.htmlを開いて読みたいコマンドレットのリンクをクリックしてください。
関連リンクに実際にリンクを張るなど、改造しだすといろいろできると思いますが、まずはワンライナーとしてここに載せておきます。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2006/11/01/43172.aspx

2006/06/30

【その一】
ディレクトリにあるbbbbaaaa[ファイル番号].extのようになっているファイル名をaaaabbbb[ファイル番号].extのようにリネームしたいと思ったので、PowerShellを使おうと思いました。なかなか-replace演算子の使い方が思いつかなかったのですがこんな感じでいけました。

dir|foreach{ren $_ ($_ -replace "bbbbaaaa","aaaabbbb")}
各Cmdletのエイリアスは次のとおり。
  • dir = get-childitem
  • foreach,% = foreach-object
  • ren = rename-item

【そのニ】
ディレクトリにあるaaaその1.ext , bbbその2.ext・・・というファイルを、aaaその01.ext , bbbその02.extのようにリネームしたいと思います。

dir|% { [void] ($_ -match "(?
.*その)(?\d+)(?.*)");ren $_ ($matches["pre"] +"{0:d2}" -f [int]$matches["num"]+ $matches["post"])}

こんなのを考えましたが暗号みたいになってしまいましたね…。もっといい方法があれば教えてください。

ちょっとずつですがCmdletの使い方を学んでいけたらなーと思います。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2006/06/30/31519.aspx

新しい記事のページへ


Copyright © 2005-2018 Daisuke Mutaguchi All rights reserved
mailto: mutaguchi at roy.hi-ho.ne.jp
プライバシーポリシー

Twitter

Books