2011年5月30日月曜日

◆空のフォルダーを抽出する

001
002
003

dir d: -recurse | 
 
? { $_.PSIsContainer -and !$_.GetFiles().Count -and
  !$_.GetDirectories().Count} | %{ $_.FullName }

上記サンプルではDドライブ配下のファイルとフォルダーを再帰しながら検索し、フォルダーだったらGetFilesメソッドとGetDirectoriesメソッドで中身があるかを判定しているだけ。

2011年5月27日金曜日

◆参照モードでのテキストファイルの読み込み

テキストファイルを読み込むには言わずと知れたGet-Contentコマンドレットを使う。
通常はこれで全く問題ないと思うが、特別性能を要求されるような処理では、System.IO.FileクラスのReadAllTextメソッドを使うと良い。

ただし、このメソッドは排他されているファイルの読み込みが出来ない。
そんな時は、StreamReaderクラスを使い、アクセスモードを指定する。

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

#Measure-Command{
# [System.IO.File]::ReadAllText("$env:windir\windowsupdate.log")
#}


Measure-Command{
gc $env:windir\windowsupdate.log
}

Measure-Command{
$mode = [System.IO.FileMode]::Open
$access = [System.IO.FileAccess]::Read
$fileshare = [System.IO.FileShare]::ReadWrite

$file = [System.IO.File]::Open(
"$env:windir\windowsupdate.log",$mode,$access,$fileshare)

$reader = New-Object System.IO.StreamReader($file)
$reader.ReadToEnd()
$reader.Close()
$file.Close()
}

image

FileMode 列挙体
FileAccess 列挙体
FileShare 列挙体

2011年5月23日月曜日

◆デバッグ情報出力機能の付いた関数を作る(Write-Verbose)

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

function funcArgCheck{
 
[cmdletbinding()]
 
 
param
(
   
[ValidateSet("min","minmin","minminnana")]
    $name,
    [ValidateRange(10,40)]
    $age,
    [ValidateScript({Test-Path $_})]
    $path
  )

 
Write-Verbose "以下のパラメータが指定されました"
  Write-Verbose ("name = {0}" -f $name
)
 
Write-Verbose ("age = {0}" -f $age
)
 
Write-Verbose ("path = {0}" -f $path
)

 
"チェック終了"

}

funcArgCheck "min" 12 "c:\Windows" -verbose
</DI

Write-Verboseコマンドレットで情報を書きこんでおくと、-verbose スイッチで情報を出力させることが出来る様になる。

この機能を有効にするには、cmdletbinding属性の指定も必要なようだ。

image

2011年5月20日金曜日

◆特定のパラメータを持つコマンドレットを表示する

recurseパラメータを持つコマンドレットを表示するにはこんな感じ。

PS>Get-Help * -Parameter recurse

Name                              Category  Synopsis
----                              --------  --------
Get-ChildItem                     Cmdlet    1 つ以上の
Get-WmiObject                     Cmdlet    Windows Ma
Remove-Item                       Cmdlet    指定した項
Copy-Item                         Cmdlet    名前空間内

コマンド名を求めるだけならばこれでOKなのだが、実際このコマンドを使って何かをしたいとなるとこれでは不十分。

コマンド自体を取得したい場合は以下のようにすると良い。

PS>Get-Command -CommandType cmdlet | ?{$_.parameters.keys -contains "recurse"}

CommandType     Name                                                          Definition
-----------     ----                                                          ----------
Cmdlet          Copy-Item                                                     Copy-Item [
Cmdlet          Get-ChildItem                                                 Get-ChildIt
Cmdlet          Get-WmiObject                                                 Get-WmiObje
Cmdlet          Remove-Item                                                   Remove-Item

parametersはDictionaryオブジェクトのようなので、そのメソッドであるContainsKeyを使っても良いかもしれない。

PS>Get-Command -CommandType cmdlet | ?{$_.parameters.ContainsKey("recurse")}

CommandType     Name                                                          Definition
-----------     ----                                                          ----------
Cmdlet          Copy-Item                                                     Copy-Item
Cmdlet          Get-ChildItem                                                 Get-ChildI
Cmdlet          Get-WmiObject                                                 Get-WmiObj
Cmdlet          Remove-Item                                                   Remove-Ite

2011年5月17日火曜日

◆自動デフラグのイベントログを表示する

Windows7ではデフォルトで各ドライブのデフラグを自動的に行うようになっている。

その自動で行われたデフラグのイベントログを表示するサンプルがあったので紹介しておく。

Analyze automatic defragmentation - Power Tips - Powershell.com Powershell Scripts, Tips and Resources

転載するとこんな感じ。

001
002
003
004

get-eventlog -LogName Application -InstanceId 258 |
ForEach-Object {$i=1|Select-Object Date, Type, Drive;`
$i.Date = $_.TimeWritten; $i.Type,`
$i.Drive = $_.ReplacementStrings; $i }

-- 20150412 改行がむちゃくちゃでわかりづらいので修正--

001
002
003
004
005
006
007

get-eventlog -LogName Application -InstanceId 258 |
  %
 {
      
$i=1|Select-Object Date, Type, Drive
       $i.Date = $_.
TimeWritten
      
$i.Type,$i.Drive = $_.
ReplacementStrings
      
$i 
     }

------------------------------------------------------------------------------

ちょっと分かりづらいので若干補足。

$i=1|Select-Object Date, Type, Driveは、オブジェクトを作ってプロパティ(Date,Type,Drive)を追加し、$i変数に格納している。
普通に(正統に?)やるならばPowerShell: ◆カスタムオブジェクト(PsObject)を使う(New-Object)2のようにPsObjectを使うのかと思うが、ここでは簡略的にint32オブジェクト(1)にプロパティを追加して使っている。

また、3,4行目では、
$i.Type,$i.Drive = $_.ReplacementStrings
でTypeプロパティとDriveプロパティにReplacementStringsの配列の値をそれぞれ格納している。
これは、
$a,$b = 1,2
とやって、$aには1、$bには2が代入されるのと同じだ。

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

image

2011年5月16日月曜日

◆IISを管理する

IISがインストールされている環境でWebAdministrationモジュールをインポートする。
Get-Commandでコマンドの一覧を見ると*-Web*という名前でコマンドが用意されていることが判る。

PS>ipmo WebAdministration
PS>gcm -Module webadministration

CommandType     Name
-----------     ----
Cmdlet          Add-WebConfiguration
Cmdlet          Add-WebConfigurationLock
Cmdlet          Add-WebConfigurationProperty
Cmdlet          Backup-WebConfiguration
Alias           Begin-WebCommitDelay
Cmdlet          Clear-WebConfiguration
Cmdlet          Clear-WebRequestTracingSettings
Cmdlet          ConvertTo-WebApplication
Cmdlet          Disable-WebGlobalModule
Cmdlet          Disable-WebRequestTracing
Cmdlet          Enable-WebGlobalModule
Cmdlet          Enable-WebRequestTracing
Alias           End-WebCommitDelay
Cmdlet          Get-WebAppDomain
Cmdlet          Get-WebApplication
Cmdlet          Get-WebAppPoolState
Cmdlet          Get-WebBinding
Cmdlet          Get-WebConfigFile
 ・
 ・


IISドライブが用意されているのでアプリケーションプールやサイトを簡単に確認することができる。
image

アプリケーションプールの再起動もこんな感じ。
image

私自身はIISの管理とかはしないのであまり使い道は無いが、管理者の方は便利に使えるのではないしょうか。

◆パラメータ属性2

PowerShell: ◆パラメータ属性の補足(追記)

上記のほかにパラメータチェックで頻繁に使われそうな属性。

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

function funcArgCheck{
   
param(
[ValidateSet("min","minmin","minminnana")]
$name
,
[
ValidateRange(10,40)]
$age
,
[
ValidateScript({Test-Path $_})]
$path
)

"以下のパラメータが指定されました"
"name = {0}"
 -f $name
"age = {0}" -f $age
"path = {0}" -f $path

}

funcArgCheck "min" 12 
"c:\Windows"
"-"
 * 10
funcArgCheck "hoge" 12 
"c:\Windows"
"-"
 * 10
funcArgCheck "minmin" 8 
"c:\Windows"
"-"
 * 10
funcArgCheck "minminnana" 15 "c:\Windows2"

image

2011年5月11日水曜日

◆2進数16進数を使う

001
002
003
004
005
006
007
008
009
010

"10進2進変換 255"
[Convert]::ToString(255,2)
"10進16進変換 255"
[Convert]::ToString(255,16)
"2進10進変換 11111111"
[Convert]::ToInt32("11111111",2)
"16進10進変換 FF"
[Convert]::ToInt32("FF",16)
"16進定数"
"0xFF={0}"
 -f 0xFF
<

image

◆イベントログから最新のエラーを表示する

前回のスクリプトを修正して、各イベントログから直近のエラーを表示してみる。

001
002
003

Get-EventLog -List  | ? {$_.entries.count} | 
 
%{$_.log;Get-EventLog $_.log -Newest 10 -After (Get-Date).AddDays(-15) `
-EntryType Error -ea 0 | Out-Host }

image

とりあえず、直近15日のMAX10件を表示させている。

◆dotNET型の出力フォーマット(DotNetTypes.format.ps1xml)

イベントログの一覧は以下のような形で取得できる。

PS>Get-EventLog -List

  Max(K) Retain OverflowAction        Entries Log
  ------ ------ --------------        ------- ---
  20,480      0 OverwriteAsNeeded      30,480 Application
  20,480      0 OverwriteAsNeeded           0 HardwareEvents
     512      7 OverwriteOlder              0 Internet Explorer
  20,480      0 OverwriteAsNeeded           0 Key Management Service
   8,192      0 OverwriteAsNeeded           0 Media Center
     128      0 OverwriteAsNeeded       2,359 OAlerts
  20,480      0 OverwriteAsNeeded      29,732 Security
  20,480      0 OverwriteAsNeeded      58,985 System
  15,360      0 OverwriteAsNeeded      19,325 Windows PowerShell

この結果から、Entriesがゼロのログを除外するには、普通に考えると以下のようになる。

PS>Get-EventLog -List | ?{$_.Entries}

  Max(K) Retain OverflowAction        Entries Log
  ------ ------ --------------        ------- ---
  20,480      0 OverwriteAsNeeded      30,480 Application
     128      0 OverwriteAsNeeded       2,359 OAlerts
  20,480      0 OverwriteAsNeeded      29,732 Security
  20,480      0 OverwriteAsNeeded      58,985 System
  15,360      0 OverwriteAsNeeded      19,325 Windows PowerShell

結果は期待通りのものが取得できているのだが、何かがおかしい。
試してみると分かるがやけに時間がかかるのである。最初の一覧表示がノータイムで帰ってきていることを思えば、そこからEntriesがゼロの4行をフィルターするだけの処理もノータイムで返ってきて然るべき。

そこで、その原因を調べてみた。

PS>Get-EventLog -List | gm Entries


   TypeName: System.Diagnostics.EventLog

Name    MemberType Definition
----    ---------- ----------
Entries Property   System.Diagnostics.EventLogEntryCollection Entries {get;}

これを見ると判るように、そもそもEntriesプロパティは

EventLogEntryCollectionを返してきている。
にも関わらず単純に画面出力したときは、その数が表示されているということはそこに何かが介在していることになる。

PowershellではdotNETのオブジェクトを出力するときにDotNetTypes.format.ps1xmlというファイルの定義に従って表示する仕掛けになっている。(パスは通常C:\WINDOWS\system32\WindowsPowerShell\v1.0)

そこでこの中身を確認してみると

Diagnostics.EventLogクラスは以下のように定義されている。

<View>
    <Name>System.Diagnostics.EventLog</Name>
    <ViewSelectedBy>
        <TypeName>System.Diagnostics.EventLog</TypeName>
    </ViewSelectedBy>

    <TableControl>
        <TableHeaders>
            <TableColumnHeader>
                <Label>Max(K)</Label>
                <Alignment>Right</Alignment>
                <Width>8</Width>
            </TableColumnHeader>
            <TableColumnHeader>
                <Label>Retain</Label>
                <Alignment>Right</Alignment>
                <Width>6</Width>
            </TableColumnHeader>
            <TableColumnHeader>
                <Label>OverflowAction</Label>
                <Width>18</Width>
            </TableColumnHeader>
            <TableColumnHeader>
                <Label>Entries</Label>
                <Alignment>Right</Alignment>
                <Width>10</Width>
            </TableColumnHeader>
            <TableColumnHeader>
                <Label>Log</Label>
            </TableColumnHeader>

        </TableHeaders>
        <TableRowEntries>
            <TableRowEntry>
                <TableColumnItems>
                    <TableColumnItem>
                        <ScriptBlock>$_.MaximumKilobytes.ToString('N0')</ScriptBlock>
                    </TableColumnItem>
                    <TableColumnItem>
                        <PropertyName>MinimumRetentionDays</PropertyName>
                    </TableColumnItem>
                    <TableColumnItem>
                        <PropertyName>OverflowAction</PropertyName>
                    </TableColumnItem>
                    <TableColumnItem>
                        <ScriptBlock>$_.Entries.Count.ToString('N0')</ScriptBlock>
                    </TableColumnItem>
                    <TableColumnItem>
                        <PropertyName>Log</PropertyName>
                    </TableColumnItem>

                </TableColumnItems>
            </TableRowEntry>
         </TableRowEntries>
    </TableControl>
</View>

Entriesプロパティは、そのCountを表示する仕掛けになっていることが解る。

そこで先のフィルターを以下のように書き換える。

PS>get-eventlog -List  | ? {$_.entries.count}

  Max(K) Retain OverflowAction        Entries Log
  ------ ------ --------------        ------- ---
  20,480      0 OverwriteAsNeeded      30,480 Application
     128      0 OverwriteAsNeeded       2,359 OAlerts
  20,480      0 OverwriteAsNeeded      29,732 Security
  20,480      0 OverwriteAsNeeded      58,987 System
  15,360      0 OverwriteAsNeeded      19,325 Windows PowerShell

すると期待通り、ノータイムで結果が帰ってきた。(最初のやり方では各Entrieの中身を全件なめて抽出が行われているのだろう)

2011年5月2日月曜日

◆一定時間以内に起動されたプロセスを取得する(New-TimeSpan)

5分以内に起動されたプロセスを取得するサンプル。

PS>ps | ?{$_.starttime} | ?{(New-TimeSpan $_.starttime).TotalMinutes -le 5}

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     92       8     2412      10992    91     0.08   5292 notepad
    120      10     3748       6920    35     0.11   4944 WmiPrvSE

Starttimeを持っていないプロセスもあるようなのでフィルターして除外している。

New-TimeSpanコマンドレットは開始時間だけ指定すると、現在時刻までのTimeSpanオブジェクトを返してくれるようだ。

2011年5月1日日曜日

◆ディレクトリー配下のファイルの中身から文字列を検索する

エクスプローラの検索ボックスで探せばすぐ見つかる。
と、思ったのだがこれがまた見つからない。
どうもファイル名だけで検索している感じだ。

中身を検索するときどうするの?
っと検索オプションなどを探してみたが見つからない。

???

まぁ良いか。とも思ったが気を取り直して調べてみると、どうやらINDEXを作っていない場所では中身まで検索しないのがデフォルトっぽい。
フォルダのオプションで中身まで検索する指定にすれば良いらしいがなんとなく面倒。

INDEXを作れば何も問題ないのだろうが、めったに検索しないのにいつもINDEX化しておくのもちょっと気が引ける。
(しかもINDEXが必要なほど範囲を広げた検索はめったにやらない。)

という事でやっぱりここはPowershellで。

dir D:\MyTest\DHTMLサンプル\part2\* -Recurse |
    Select-String "カーソル" -Encoding default

なんてことはないが、Encodingを意識する必要があるのはちょっと面倒かな。
エクスプローラならきっと自動で判別するんだろうな・・・。
(暇があったら自動判定しながら検索するなんてのもやってみたい)

ちなみに、今回検索するファイルは以下のように階層が決まった構成になっているので再帰するまでもないかも。
image

こんな感じのほうが効率的かもしれない。

Resolve-Path D:\MyTest\DHTMLサンプル\part2\*\*.htm |
    Select-String "カーソル" -Encoding default -Path {$_}