2014年10月6日月曜日

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

今更ながら基本の基本を確認しておく。

IF文での判定条件は普通に考えると、

if($a –eq 1){hogehoge}

てな感じに書くのが一般的な気がする。
ただし、これは一般的な他の言語と同様な書き方で一見何の問題も無いように見えるが、PowerShellの場合はちょっと事情が異なる。

例えば以前PowerShell: ◆配列をフィルターするで書いたように、配列などではイコール演算子が感覚とはちょっと違った動作をする。

この間、PowerShell: ◆閾値以下の空き容量のHDドライブがあったら警告するで使った抽出判定も、

if(Get-PSDrive | ?{$_.USED –ne “”}){hogehoge}

ってな感じでは旨くいかない。(余計なものが幾つか残ってしまう)
image

では、どうすればよいのか。
昔からなんとなく使っている以下の書き方が実は正しい書き方なのではと最近思っている。

if($a){hogehoge}

こう書いた場合、どう判定されるかというと(経験的に)、その変数のデータ型の初期値であればFalse、初期値以外がTrueになる。

数値であれば、0がFalse、文字列であれば””がFalse、オブジェクトであればNullがFalseといった具合。
配列がちょっと厄介だが、要素が複数の場合はTrue、ひとつの場合はその変数の属性に応じて上記と同様に判定される。

この機能はおそらく、PowerShellが$aをbool型に自動変換しているのだろう。
即ち、

if([bool]$a){hogehoge}

と書くのと同じではないかと思う。

image

 

先に書いた、

if(Get-PSDrive | ?{$_.USED –ne “”}){hogehoge}

のパターンでは、USEDプロパティがスクリプトプロパティなので、ドライブによってUSEDのデータ型が違ってきている。

なので、演算子を直接使って判定しようとすると

image

てな感じになってしまう。

これはやはり普段から

if($a){hogehoge}

の書き方を使うのが良いのだろうと思う。

ちなみに、「-ne」の場合は「!」を使って(!$a)とする。

 

あくまでも、経験則で考えたものですが・・・・。

2014年10月1日水曜日

◆閾値以下の空き容量のHDドライブがあったら警告する

>if(Get-PSDrive | ?{$_.used} | %{$_.Free / ($_.used + $_.free)} | ?{$_ -lt 0.6}){"HD容量不足発生";Read-Host}

image

ここでは閾値として0.6(60%)を指定している。

後はこれをスタートアップで実行しても良いし、サーバーなどを巡回しても良いしって感じだろうか。

---

質問があったので補足

各ドライブの空き容量を表示するにはとりあえずこんな感じで

>Get-PSDrive | ?{$_.used} | select name , @{name="残容量(%)";expression={[int](($_.Free / ($_.used + $_.free))*100)}}

image

2014年9月26日金曜日

◆Foreach-Objectのパラメータへの疑問

今更の感じはするが、Foreach-Objectの動作。

PS>1..3 | ForEach-Object{"開始"}{"ループ"}{"終了"}
開始
ループ
ループ
ループ
終了

本来、ヘルプを見ると位置オペランドは(-Process)だけなので、なぜこれで動くのだろうとちょっと疑問に思う。

ちなみに、以下は良いが、

PS>1..3 | ForEach-Object{"開始"}{"ループ"}

当然、以下は上と同じ書式なので「ループ」が初期処理とみなされる。

PS>1..3 | ForEach-Object{"ループ"}{"終了"}

なので、

PS>1..3 | ForEach-Object{"ループ"}  -end{"終了"}

とする必要が有りそう。

2014年9月11日木曜日

◆Get-Helpの怪

なぜか以下のようなエラーが発生する。

PS>get-help  演算子
Get-Help : 型 [Microsoft.ActiveDirectory.Management.ADUser] が見つかりません。この型を含むアセンブリが読み
ことを確認してください。
発生場所 行:1 文字:9
+ get-help <<<<  演算子
    + CategoryInfo          : InvalidOperation: (Microsoft.Activ...nagement.ADUser:String) [Get-Help]、Ru
    + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.GetHelpCommand

普通は「演算子」という文字列が含まれるヘルプが一覧表示される。

image

さ~、なんとしたものか・・・。

自分だけがおかしいということは「Profile」・・・。

案の定、プロファイル無しで起動したらエラーは発生しなかった。

さぁ、ではプロファイルの何が悪いのか・・・。

なんだかんだ使わない物やテスト的に入っているものもありプロファイルは結構な量になっている。

と嘆いても仕方が無いので2分割方?で探していく・・・・・・・・・・・・・・・・・・・。

やっと見つけた、これだ!!

$env:path += ";D:\svndoc\PS\;.\"

ん~、スクリプトの入ったフォルダをPath環境変数に追加しているだけだ。

もしかして、このスクリプトの中もヘルプで探しに行ってしまうのか?

Version3で同じ環境にして試した見たが再現しなかった。

という事は「バグ」か。(><)

2014年8月26日火曜日

◆検索文字列をハイライト表示する

環境変数に目的のパスが含まれているか確認したいことは良くあるが、結構なが~いのが普通なので年寄が目視で確認するのはちと辛い。

メモ帳に貼り付けて検索すれば事足りるのだが、やっぱりここはPowerShellでやってみたい。

幸いファイル群から文字列を検索してハイライト表示するスクリプトが以下で公開されていた。
Wes' Puzzling Blog - Powershell script to find strings and highlight them in the output

(通常はこちらのほうが使いでがあると思われる。)

とりあえずこれをそのまま利用させていただいて単純な文字列からのパターン検索に変更した。
やっつけで変更したので中身はほとんど理解していない・・・。

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
029
030
031
032
033
034

function Write-HostAndHighlightPattern(
  
[string] $pattern = ""
  , [string] $inputtext = ""
  , [switch] $caseSensitive = $false
)
{

 
if ($pattern -eq $null -or $pattern -eq ""
) { 
   
Write-Error "Please provide a search pattern!" ; return
 }

 
$regexPattern = $pattern
  if($caseSensitive -eq $false) { $regexPattern = "(?i)$regexPattern"
 }
 
$regex = New-Object System.Text.RegularExpressions.Regex $regexPattern

  $index = 0
  while($index -lt $inputText.
Length)
  {
   
$match = $regex.Match($inputText, $index
)
   
if($match.Success -and $match.Length -gt 0
)
    {
     
Write-Host $inputText.
SubString(
       
$index, $match.Index - $index) -nonewline
      Write-Host $match.Value.ToString() -Foreground Red -nonewline
      $index = $match.Index + $match.
Length
    }
   
else
    {
     
Write-Host $inputText.SubString($index) -nonewline
      $index = $inputText.
Length
    }
  }
}


Write-HostAndHighlightPattern "system32" $env:path

結果

image

2014年8月22日金曜日

◆Measure-Objectで日付型のMAXは取得できない

WindowsUpadateの最終更新日を取得する。

Get-HotFix | Measure -Maximum -Property InstalledOn

てな感じで取得できそうな気がする。(実際、Windows8では取得できる)

しかし、Windows7では以下のようなエラーが・・・。

image

えぇー、数値しか駄目なの(><)

仕方がないので力技(一旦数値に変換してから戻す)

PS>[datetime][long]((Get-HotFix | select {($_.installedon).ticks} | measure -Maximum -Property '($_.installedon).ticks').Maximum)

まだ、以下のほうが良いか

PS>(Get-HotFix | sort installedon -Des | select -First 1).InstalledOn

ちゃんとやっている方もいらっしゃる
Measure-Latest: finding latest date/time | Dmitry's Blog: Cloud, PowerShell and beyond

 

Windows8が大コケしたおかげで、PowerShellも2.0がまだまだ幅を利かせてしまうので、ちょっと悩ましい。

2014年8月20日水曜日

◆変数の値解決?

ネットで見かけた質問で以下のようなものがあった。

$drive = ""
$cmd = "dir $drive"
$drives = "c:","d:"

foreach($drive in $drives)
{
    Invoke-Expression $cmd
}

なんとなく眺めるとうまくいくような気もする。

Invoke-Expressionをやめて単純に$cmdを表示させると、

PS>$drive = ""
$cmd = "dir $drive"
$drives = "c:","d:"

foreach($drive in $drives)
{
    $cmd
}

-- 以下、結果--

dir
dir

 

これまた意外と嵌れば嵌るのかもね・・・っと思ったり。

要は2行目の、
$cmd = "dir $drive"

のときに、$driveは値解決されちゃうので$cmdの中身は”dir”になっちゃう。

どうすれば良いんですかね・・・。

こんな感じ?

$drive = ""
$cmd = "dir {0}"
$drives = "c:","d:"

foreach($drive in $drives)
{
    Invoke-Expression ($cmd -f $drive)
}


もしかして、こんなのがスマートか?

$drive = ""
$cmd = {dir $drive}
$drives = "c:","d:"

foreach($drive in $drives)
{
   
   & $cmd
}