2012年5月31日木曜日

◆ActiveDirectoryコマンドレットでFilterの記述方法

Filterの記述方法が良く分からなかったので纏めておく。

サンプルを見るとこんな感じにシングルコーテーションで囲んでいる。

PS>Get-ADUser -filter 'SamAccountName -like "test*"' | select SamAccountName

SamAccountName
--------------
test
test2

ダブルコーテーションとシングルコーテーションを入れ替えても大丈夫。

PS>Get-ADUser -filter "SamAccountName -like 'test*'" | select SamAccountName

SamAccountName
--------------
test
test2

{}で囲んであげるサンプルも載っていた。

PS>Get-ADUser -filter {SamAccountName -like "test*"} | select SamAccountName

SamAccountName
--------------
test
test2

コマンドを組み合わせると条件は変数指定になることが多くなると思うので変数でも試してみる。

PS>$st = "test*"


シングルコーテーションはOK

PS>Get-ADUser -filter 'SamAccountName -like $st' | select SamAccountName

SamAccountName
--------------
test
test2


{}で囲むのもOK

PS>Get-ADUser -filter {SamAccountName -like $st} | select SamAccountName

SamAccountName
--------------
test
test2


ダブルコーテーションはエラーになる

PS>Get-ADUser -filter "SamAccountName -like $st" | select SamAccountName
Get-ADUser : クエリ解析エラー: 'SamAccountName -like test*' エラー メッセージ: 'syntax error' 場所: '22'。
発生場所 行:1 文字:11


こんな感じにコーテーションの追加が必要みたい

PS>Get-ADUser -filter "SamAccountName -like '$st'" | select SamAccountName

SamAccountName
--------------
test
test2

 

次は変数が以下のようなプロパティの場合。

PS>($st = 1 | select prop).prop = "test*"
PS>$st.prop
test*

シングルコーテーションはエラーになる。

PS>Get-ADUser -filter 'SamAccountName -like $st.prop' | select SamAccountName
Get-ADUser : 種類: 'System.Management.Automation.PSCustomObject' のオブジェクトでプ
発生場所 行:1 文字:11
+ Get-ADUser <<<<  -filter 'SamAccountName -like $st.prop' | select SamAccountName
    + CategoryInfo          : InvalidArgument: (:) [Get-ADUser]、ArgumentException
    + FullyQualifiedErrorId : 種類: 'System.Management.Automation.PSCustomObject'
見つかりません。,Micros    oft.ActiveDirectory.Management.Commands.GetADUser

{}で囲んでも駄目

PS>Get-ADUser -filter {SamAccountName -like $st.prop} | select SamAccountName
Get-ADUser : 種類: 'System.Management.Automation.PSCustomObject' のオブジェクトでプ
発生場所 行:1 文字:11
+ Get-ADUser <<<<  -filter {SamAccountName -like $st.prop} | select SamAccountName
    + CategoryInfo          : InvalidArgument: (:) [Get-ADUser]、ArgumentException
    + FullyQualifiedErrorId : 種類: 'System.Management.Automation.PSCustomObject' の
見つかりません。,Micros    oft.ActiveDirectory.Management.Commands.GetADUser

当然ダブルコーテーションも駄目

PS>Get-ADUser -filter "SamAccountName -like $st.prop" | select SamAccountName
Get-ADUser : クエリ解析エラー: 'SamAccountName -like @{prop=test}.prop' エラー メッ
発生場所 行:1 文字:11
+ Get-ADUser <<<<  -filter "SamAccountName -like $st.prop" | select SamAccountName
    + CategoryInfo          : ParserError: (:) [Get-ADUser]、ADFilterParsingExcept
    + FullyQualifiedErrorId : クエリ解析エラー: 'SamAccountName -like @{prop=test}
or' 場所: '22'。,M    icrosoft.ActiveDirectory.Management.Commands.GetADUser

どうすればよいんでしょうね。
まぁ、一旦変数に入れれば良いだけではあるのですがなんとなく残念な気持ち。

プロパティをFilterに使うことは多くないのかもしれませんが、配列要素なんかも同様に駄目そうなので・・・。

かろうじてダブルコーテーションで式の評価を強制させた場合はうまくいく。

PS>Get-ADUser -filter "SamAccountName -like '$($st.prop)'" | select SamAccountName

SamAccountName
--------------
test
test2

ちょっと場当たり的に試しただけで理由が分かっていないのでいずれ機会があったらもう少し調べてみたい。

◆タイトルバーに時計表示を追加する

役に立つかというと微妙かもしれないがスクリプト自体は面白いのでメモしておく。

Adding Clock to PowerShell Console - Power Tips - Powershell.com – Powershell Scripts, Tips and Resources

2012年5月30日水曜日

◆環境変数から特定のパスを削除する

Extract Paths from Strings Like Environment Variables - Power Tips - Powershell.com – Powershell Scripts, Tips and Resources

>(($env:path -split ';') -ne 'C:\Windows\system32') -join ';'

一旦配列に分解してから該当のパスをフィルターし、再度配列に戻すというパターンのようだ。

特にテクニック的なところは無いが、配列に対してeqやneなどの演算子を使うとフィルターできるという所だろうか。PowerShell: ◆配列をフィルターする

直感的には以下のような感じでも良いのかなという気もする。

PS>$path = "aa;bb;cc;dd"
PS>$path -replace ';cc|cc;'
aa;bb;dd
PS>$path -replace ';aa|aa;'
bb;cc;dd

2012年5月24日木曜日

◆配列の連結

配列の連結は今までも何度も出ていますし今更なのですが若干紛らわしいのでおさらいを。

以下の様にカンマで繋げてもなんとなく良さそうに見える。
image

しかし、通常は配列の連結には「+」を使う。
image

パイプして使ってみても一見違いが見えない。
image

しかしこの違いを把握しておかないと???といった事態に陥る事がある。

 

image

これは予想通り。

でももう一方は、
image

っと何やら???な結果を返してくる。

まぁ、これが当然だよねと思えればPowerShell免許皆伝?
(そんな大げさな話ではないか・・・)

まぁ、逆に言えばこの結果を意図して使う場合もあるだろう。

一応初心者の方のために解説しておくと、「$a+$b」の方はパイプラインに渡っていくのは個々の要素であり、一方「$a,$b」でパイプラインに渡っていくのは「$a」と「$b」そのものである。
image

なので、「$a,$b」の場合のパイプライン中での処理は「$a + 1」と「$b + 1」となる。
「$a + 1」は「$a」の配列の最後に「1」を追加して「1,2,3」が「1,2,3,1」となり「$b + 1」では「4,5,6」が「4,5,6,1」、両方合わせると「1,2,3,1,4,5,6,1」となるのである。

2012年5月23日水曜日

◆PCの起動終了時刻を表示する

以前PowerShell: ◆最近何時に帰ったか調べる(shutdownイベントを拾う)で終了時刻を表示するのはやったことがあるのだが、ふとしたところで起動時間も一緒に表示したいと要望されて作ってみた。

終了時刻が表示できているので起動時刻を表示するのも特に問題は無い。

それぞれを別に表示するのであれば「イベントID」を変えて実行するだけ。

しかし、通常は「起動」と「終了」を同一行に表示させたいというのが人情だろう。
まぁそれも特に問題は無いだろうと思ったのだが意外と簡単な方法が浮かばない。

片方に「日付と起動時間」、もう片方に「日付と終了時間」があり、これを一行に纏める。
数十年前の遠い昔によくやったバッチ処理のような問題だ。
実際にこれがバッチ処理なら何も迷うことは無い典型的なパターン。
それぞれを日付でソートしてマッチングしてくっつけながら出力していけば良い。

しかし、PowerShellではどうすればスマートなんでしょうね・・・。

とりあえず私は「Group-Object」を使ってグルーピングしてやるケースが多い。
こんな感じ。

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

$startID = 2147489653;$endID = 2147489654
Get-EventLog system -InstanceId $startID,$endID
 `
 
-After ((Get-Date).AddMonths(-1)) | select timewritten,InstanceId |
 
   
group {($_.TimeWritten).ToString("yyyyMMdd")} | sort name | %
{
     
$obj = New-Object PsObject -Property @{日付=$_.name;起動="";終了=""
}
     
$_ | select -ExpandProperty group | %
{
       
if($_.InstanceId -eq $startID
){
     
$obj.起動 = $_.TimeWritten.ToString("HH:mm:ss"
)}
       
if($_.InstanceId -eq $endID
){
     
$obj.終了 = $_.TimeWritten.ToString("HH:mm:ss"
)}
      }
     
$obj | select 日付,起動,終了
  }
<!--End

一日に複数回起動することを考慮しなければこれで良いのだが、通常はPCを再起動することもあるだろう。
その場合は、起動のイベントと終了のイベントをそれぞれ再度グルーピングして最も早いものと最も遅いものを抽出する必要がある。

ちょっとネストが深くなって気乗りがしない・・・。
処理時間がシビアな場合は仕方がないがもう少し簡単に行きたい。

というわけでPowerShell: ◆最近何時に帰ったか調べる(shutdownイベントを拾う)と同じロジックで起動時間を取得しそれぞれをくっつけることにした。

</TBOD
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028

$startID = 2147489653;$endID = 2147489654
$log = Get-EventLog system -InstanceId $startID,$endID
 `
 
-After ((Get-Date).AddMonths(-1))
$StartObj = $log | ?{$_.InstanceId -eq $startID} | select timewritten |
 
 
sort timewritten | group {($_.TimeWritten).day} | %
{
   
$startTime = (@($_.group)[0]).
TimeWritten
   
New-Object PsObject -Property @{日付=$startTime.ToString("yyyyMMdd"
)
                  起動
=$startTime.ToString("HH:mm:ss"
)}
} 

$EndObj = $log | ?{$_.InstanceId -eq $endID} | select timewritten |
  sort timewritten | group {($_.TimeWritten).day} | %
{
   
$endTime = (@($_.group)[-1]).
TimeWritten 
   
New-Object PsObject -Property @{日付=$endTime.ToString("yyyyMMdd"
)
                  終了
=$endTime.ToString("HH:mm:ss"
)}
}


$list = $StartObj+$EndObj | sort 日付 | group 日付 | %
{
 
$outObj = 1 | select 日付,起動,終了
  $outObj.日付 = $_.
name

 
$_ | select -ExpandProperty group | %
{
   
if($_.起動){$outObj.起動 = $_.
起動}
   
if($_.終了){$outObj.終了 = $_.
終了}
  }
 
$outObj
}
$list | ft -AutoSize
Read-Host please Enter

Shutdownイベントを拾うのが実質1行で済んでいたのと比べるとなんか大きくなりすぎている感じはするが・・・。
image

あれ、今日と1か月前と日付がぶつかっているのかな。
5行目と11行目は「Day」プロパティではなく「ToShortDateString()」メソッドですかね。

2012年5月17日木曜日

◆ローカルのAdministrator権限が有るか確認する

 

net localgroup Administrators

netコマンドを使って「Administrators」グループのユーザー一覧を取得し、「eq」演算子で配列に含まれているか確認するって方法だ。

取り立ててTipsっぽい技は使っていないように見えるがやけに絶賛されている。

配列に含まれているかを確認したいだけであれば「contains」演算子を使った方が簡単かもしれない。

(net localgroup Administrators) -contains $env:username

◆ログオンしているユーザーを表示する

ログオンしていると必ず「Explorer」のプロセスが使用されるので、そのユーザーを一覧表示するという仕組み。

001
002
003
004

Get-WMIObject Win32_Process -filter 'name="explorer.exe"' | 
 
% { $owner = $_.
GetOwner()
     
'{0}\{1}' -f $owner.Domain, $owner.
User 
    } 
| Sort-Object -Unique

image

2012年5月7日月曜日

◆プロダクトキーを表示する

ネットを見ていたらインストール済みWindowsのプロダクトキーを表示するサンプルが紹介されていた。

私自身は必要になったことは無いのだが、調べてみるとこいつを表示するフリーソフトなんかもあったりするのでそれなりに需要はあるのかもしれない。

そんなわけで一応メモしておく。

Getting Windows Product Key - Power Tips - Powershell.com Powershell Scripts, Tips and Resources

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

function Get-ProductKey {   
   
$map="BCDFGHJKMPQRTVWXY2346789"
    $key = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion"
    $value = (get-itemproperty $key).digitalproductid[0x34..0x42]
 
   
$ProductKey = ""
 
   
for ($i = 24; $i -ge 0; $i--
) { 
     
$r = 0
 
     
for ($j = 14; $j -ge 0; $j--
) { 
       
$r = ($r * 256) -bxor $value[$j]
 
       
$value[$j] = [math]::Floor([double]($r/24
)) 
       
$r = $r % 24
 
      } 
     
$ProductKey = $map[$r] + $ProductKey
 
     
if (($i % 5) -eq 0 -and $i -ne 0
) { 
       
$ProductKey = "-" + $ProductKey
 
      } 
    } 
   
$ProductKey
} 
Get-ProductKey

また、同様のサンプルで「Office」のプロダクトキーなんかも表示できるようだ。
Finding Office and Windows Keys with Powershell