2014年03月30日

PowerShellでSYLOGを受信する

 ネットワーク機器やサーバーの動作確認をしたいときなど、WindowsでSYSLOGサーバーを起動させたくなることがあります。Vectorなどでダウンロードしてくるという方法もありますが、PowerShellを使えばWindowsの標準機能だけで動作させることができます。

 SYLOGはUDPを使ってアスキー文字列を送信しているだけなので、他のプロトコルと比べて非常に簡単にサーバーを作ることができます。
$udp = New-Object Net.Sockets.UdpClient -ArgumentList 514
$sender = $null

while($true) `
{
  if($udp.Available) `
  {
    $buffer = $udp.Receive([ref]$sender)
    $sender.ToString()+[Text.Encoding]::UTF8.GetString($buffer)
  }
  [Threading.Thread]::Sleep(500)
}

 これを実行するとSYSLOGを受信するたびに、受信したデータと送信元情報を表示します。
192.168.0.1:54677<141>[00:00:00:00:00:00] ABCDEFGHIJKLMN ntpclient: SUCCESS: set time : Sun Mar 30 17:27:37 2014
 途中の<141>はSYSLOGのプロトコルに規定されている部分で、FacilityやSeverityを表しています。141を8で割った余りは5ですが、これはSeverity(深刻度)を表しています。このログは5なので「通知」を意味します。141を8で割った商は17ですが、これはFacilityを表していてます。Facilityはログの種類を表していて、Facilityの17はlocal1です。ちゃんとしたSYSLOGサーバーならFacilityの値によって出力先のログファイルを分けることができます。

 <141>より前の部分は送信元のIPアドレスとポート番号です。送信されてきたUDPデータグラムのIPヘッダー部分に書かれていた情報をここで表示しています。

 <141>より後の部分は実際のSYSLOGのメッセージです。

 FacilityとSeverityの値をそれぞれ以下の表に示します。

Facility
コードFacility
0カーネル
1ユーザー・レベル
2電子メール
3システム・デーモン
4セキュリティー・認証
5syslogd内部生成
6ラインプリンター
7ネット・ニューズ
8UUCP
9クロック・デーモン
10セキュリティー・認証
11FTP(ファイル転送)
12NTP(時刻合わせ)
13監査
14警戒
15クロックデーモン
16local0
17local1
18local2
19local3
20local4
21local5
22local6
23local7

Severity
コードSeverity
0Emergency
1Alert
2Critical
3Error
4Warning
5Notice
6Informational
7Debug


 ここからはスクリプトを解説していきます。

 まず、最初の行は.NET Frameworkのクラス呼び出しであり、System.Net.Sockets.UdpClientクラスのオブジェクト(実体)を生成しています。このとき、-ArgumentList 514の指定は解放するポート番号を意味しています。
$udp = New-Object Net.Sockets.UdpClient -ArgumentList 514

 2行目は送信元を格納するための変数を用意しています。
$sender = $null

 while($true)は無限ループを意味するので、Ctrl+Cなどユーザー操作で終了させられるまで{ }の中身をくり返します。
while($true) { (中略) }

 $udp.Availableは、受信したUDPデータグラムがあるときに$trueになります。UDPデータグラムを読むだけなら後述する$udp.Receiveを実行すれば良いのですが、受信していない状態で$udp.Receiveを実行すると受信待ちになり、受信するまではCtrl+Cを入力しても終了できなくなります。そこで、受信したかどうか判定してから読み込むようにします。
if($udp.Available) { (中略) }

 $udp.Receiveの行では、受信したUDPデータグラムを読み込みます。読み込んだ内容は返却値として得られるので$bufferに格納され、送信元のIPアドレスやポート番号は引数の$senderに格納されます。$udp.Receive()で行われる処理は$senderの値変更を含んでいるので、$senderには[ref]をつけなければなりません。
$buffer = $udp.Receive([ref]$sender)

 次の行は、送信元IP、ポート番号、および受信したUDPデータグラムを表す文字列です。$senderは送信元のIPとポート番号を表すデータなので、$sender.ToString()で、ポート番号を表す文字列が得られます。その後ろは、受信したUDPデータグラムである$bufferをUTF8の文字列とみなして、その文字列を出力することを意味します。+で、その前後の文字列を連結します。
$sender.ToString()+[Text.Encoding]::UTF8.GetString($buffer)

 この行は、500ミリ秒待つことを意味します。これを入れないと、受信していない間は空っぽの無限ループを実行することになってしまうので少し待ってから受信するようにしています。
[Threading.Thread]::Sleep(500)
web拍手 by FC2
posted by 北条利彦 at 19:54 | Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2013年12月05日

PowerShellでobject型以外の配列を生成する

 C#などのプログラミング言語では配列のオブジェクトを生成するための書き方は簡単です。PowerShellでも@()を使えばobject型の配列を生成できますが、そのほかの型の配列を生成する書き方はなかなか思いつかないかも知れません。

 次のように記述することで、byte[]型で任意の長さを持つ配列を生成することができます。もちろん、他の型の配列もやり方は同じです。

PS U:\> $buffer = New-Object byte[] -ArgumentList 10
PS U:\> $buffer.Length
10
PS U:\> $buffer.GetType().FullName
System.Byte[]
PS U:\>

 配列が生成できると、[IO.Stream]クラスなどのストリームからデータを読み出して格納するのに役に立ちます。
web拍手 by FC2
posted by 北条利彦 at 22:06 | Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2013年11月28日

PowerShellで例外オブジェクトを取得する

 C#などで例外を補足したとき、例外オブジェクト(Exceptionクラスの派生クラスのオブジェクト)を次のように取得できます。

catch(Exception ex)


 PowerShellでは、catchではなくtrap構文を使用しますが、上記のexに相当する変数宣言をする箇所がありません。その代わり、$_変数に自動的に代入されています。

PS C:\> & { 1/$null; trap { "例外が発生しました"+$_ ; continue } }
例外が発生しました0 で除算しようとしました。
PS C:\>
web拍手 by FC2
posted by 北条利彦 at 07:39 | Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2011年09月19日

[PowerShell] 成分解析を行う

はじめに
 一時期流行した「成分解析」をPowerShellで実装してみました。
人気ブログランキング

オブジェクトの生成
$analysis = New-Object PSObject
$analysis | Add-Member NoteProperty IngredientNames `
  @(
     "アルコール","エネルギー","おいしい水","おとぎ話",
     "かさぶた","スリル","ぜい肉","みんなの願い",
     "やましさ","ワガママ","哀愁","安心感","羽毛","下心",
     "我慢","怪しさ","勘違い","感動","甘味料","岩石",
     "奇跡","気合い","強さ","筋肉","金目の物","苦労",
     "孤独","黒い計画","骨","子どもたちの夢","思いやり",
     "思い出","自由","秋の味覚","出来心","女らしさ",
     "消毒液","情熱","真心","清き一票","憎しみ",
     "大自然の恵み","大人の秘密","誰かの犠牲","男らしさ",
     "天のお告げ","天使の祝福","天然ボケ","怒り","毒物",
     "鈍感力","忍耐","粘膜","農作物","美しさ","魅力",
     "命の危険","優しさ","勇気","裏切り","涙"
   )
$analysis | Add-Member ScriptMethod CreateRandomUniqueValues `
  {
    param([int]$length,[int]$max,[Random]$random)
    $values = @()
    while($values.Count -lt $length)
    {
      $value = $random.Next($max-$values.Count+1)
      for($i = 0; $i -lt $values.Count; $i++)
      {
        if($value -ge $values[$i])
        {
          $value++
        }
      }
      $values += $value
      [Array]::Sort($values)
    }
    return $values
  }
$analysis | Add-Member ScriptMethod CreateRandomDevidedValues `
  {
    param([int]$sourceValue, `
      [int]$divisionCount,[Random]$random)
   
    $results = @()
    $rateRest = $sourceValue
   
    for($i=0; $i -lt $divisionCount; $i++)
    {
      $max = $rateRest-($divisionCount-$i-1)
      if($i -eq $divisionCount-1) { $rate = $rateRest }
      else { $rate = $random.Next($max)+1 }
      $results+=$rate
      $rateRest-=$rate
    }
    return $results
  }
$analysis | Add-Member NoteProperty ElementsMaxCount 5
$analysis | Add-Member ScriptMethod AnalyseRaw `
  {
    param([string]$name)
    $trimedName = $name.Replace(" ","").Replace(" ","")
   
    $random = New-Object Random $trimedName.GetHashCode()
    $elementsCount = $random.Next($this.ElementsMaxCount)+1
   
    $rates = @($this.CreateRandomDevidedValues( `
      100,$elementsCount,$random))
    $indices = @($this.CreateRandomUniqueValues( `
      $elementsCount,$this.IngredientNames.Length-1,$random))

    $results = @()
    for($i=0; $i -lt $elementsCount; $i++)
    {
      $result = New-Object PSObject
      $result | Add-Member NoteProperty Rate $rates[$i]
      $result | Add-Member NoteProperty Element `
        $this.IngredientNames[$indices[$i]]
      $results+=$result
    }
    $results | Sort-Object Rate -Descending
  }
$analysis | Add-Member ScriptMethod Analyse `
  {
    param([string]$name)

    $result = @($this.AnalyseRaw($name))
    if($result.Length -eq 1)
      { $format = "{0}はすべて{2}でできています。" }
    else
      { $format = "{0}の{1}%は{2}でできています。" }
   
    $this.AnalyseRaw($name) `
      | %{ $format -f $name,$_.Rate,$_.Element }
  }


実行例
 ちゃんと分析できています。やろうとすれば結果をある程度調整することもできますが、そのようなコードは入れていません。

PS U:\> $analysis.Analyse("谷垣禎一")
谷垣禎一の75%はやましさでできています。
谷垣禎一の17%は筋肉でできています。
谷垣禎一の6%は優しさでできています。
谷垣禎一の1%は怪しさでできています。
谷垣禎一の1%は子どもたちの夢でできています。
PS U:\>
web拍手 by FC2
posted by 北条利彦 at 16:24 | Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2011年01月25日

PowerShell入門

■基本中の基本

 まずPowerShellのHelloWorldを実行してみます。Windowsのコマンドプロンプト(Cmd.exe)やLinuxのbashなど、従来のコマンドライン環境ではechoコマンドを使用しましたが、PowerShellでそのようなものは不要です。

PS U:\> "Hello World"
Hello World
PS U:\>

 PowerShellでは「表れたデータは行き着く先がなければ画面に出力される」という性質があります。上記の場合、二重引用符""で囲んでいる部分は文字列のデータとみなされます。この文字列を変数に代入するような命令はしていませんし、パイプラインに流す命令もないので、行き着く先がなくなり、画面に出力されたというわけです。
人気ブログランキング

 これと同じ理由で、次の場合も画面に出力されます。
PS U:\> 1     # 整数
1
PS U:\> 2.1   # 実数
2.1
PS U:\> 6.3/3 # 式
2.1
PS U:\>

 式を与えるとそのまま計算されます。exprコマンドやそれに相当するコマンドレットを書く必要はありません。

 変数の代入は次のようにします。代入するときも$が必要です。数値を直接書いた場合と同じように、変数だけそのまま書いた場合も、やはり画面に出力されます。

PS U:\Work> $x = 2.0
PS U:\Work> $x
2
PS U:\Work>

■オブジェクト指向の側面

 従来のCUIとPowerShellの最も大きな違いは、PowerShellにはオブジェクト指向プログラミングの要素が入っていることだと思います。先ほどまで「文字列のデータ」などと書いてきましたが、PowerShellの文字列は単なるデータではなく文字列型のオブジェクトです。整数や実数もそれぞれオブジェクトです。

 それでは単なるデータとオブジェクトでは何が違うのでしょうか。オブジェクトには、自分自身に関する情報であるプロパティや、自分自身と関係する何らかの処理であるメソッドが結びついているのが、単なるデータとの違いです。プロパティやメソッドなど、オブジェクトに結びついているものを、そのオブジェクトの「メンバー」と呼びます。

 オブジェクトには「型」があり、その型によって使用できるプロパティやメソッドが異なってきます。つまり、文字列型(String型)のオブジェクトはString型のプロパティやメソッドが、整数には整数型(int型)のメソッドやプロパティが結びついています。

■プロパティの例

 では、そのプロパティを実際に見てみます。

PS U:\Work> ("abcdefg").Length
7
PS U:\Work>

 文字列、つまりString型オブジェクトである"abcdefg"を( )で囲み .Length をつけたところ、7と出力されました。この「7」は、文字列の長さを表しています。

 既に何度も書きましたが、PowerShellでは文字列もオブジェクトです。そして、自分自身(文字列)の長さを表す「プロパティ」をもっています。上記の書き方をすると、String型オブジェクトのLengthプロパティを取得できるのです。

 従来のCUIや、オブジェクト指向でないプログラミング言語では、文字列の長さを計算するコマンドや関数を呼び出して文字列長を取得していました。PowerShellは、文字列自身が持っているプロパティを参照することで、文字列長を取得するのです。

■メソッドの例

 さて、今度はメソッドの呼び出しをしてみます。先ほども書きましたが、メソッドとはオブジェクトに結びついている処理であり、オブジェクト自身を使って何か行うものです。

 String型には多くのメソッドがついていますが、その1つに、文字列の一部が置換された文字列を新たに生成するものがあります。とりあえずまたコマンドを実行してみましょう。

PS U:\Work> ("abcdefg").Replace("cde","01234")
ab01234fg
PS U:\Work>

 文字列中の"cde"の部分が"01234"に置き換わった文字列が出力されました。

 メソッドの概念の説明とはあまり関係がないのですが、上の結果は、"abcdefg"の一部が置換されたのではありません。"abcdefg"の一部が置き換わった文字列が、新たに作られたのです。この違いは変数にしてみると分かりやすいかと思います。

PS U:\Work> $text = "abcdefg"
PS U:\Work> $text.Replace("cde","01234")
ab01234fg
PS U:\Work> $text
abcdefg
PS U:\Work>

 文字列型のオブジェクトが格納された変数である $text のReplace()メソッドを呼び出し、"ab01234fg"が出力されました。しかし、その後で $text を出力すると、元の文字列がそのまま表示されました。つまり、$textに格納された文字列は全く変わっていないのです。Replace()メソッドに限らず、String型のオブジェクトを代入した変数は、別のオブジェクトを代入しない限りは、ずっと同じ文字列が入ったままです。このことは頭に入れておくと役に立つことがあるかも知れません。

■型の違い

 話は変わりますが、Get-Itemコマンドレットにパスを指定すると、ファイル(FileInfo型)やフォルダー(DirectoryInfo型)のオブジェクトを取得できます。

PS U:\> Get-Item U:\Style.css

    ディレクトリ: U:\

Mode      LastWriteTime Length Name
----      ------------- ------ ----
-a--- 2010/07/24   8:16   3296 Style.css

PS U:\>

 ここで、「Mode」など表の列名になっているのはFileInfo型オブジェクトのプロパティです。「Length」というプロパティもあります。文字列のLengthプロパティは文字列長を表していましたが、ファイルのLengthプロパティはそのファイルのバイト数です。

 文字列の長さもファイルの長さも言葉の上ではどちらも「長さ」なので上記の動作は理にかなっています。しかし、機能の上でこの2つは全く違うものです。オブジェクト指向では、同名のプロパティであっても、結びついているオブジェクトによって適切に動作が切り替わるようになっています。

 FileInfoのLengthもプロパティには違いないので、( )で囲んで .Lengthをつけるとその値だけ取得できます。

PS U:\> (Get-Item U:\Style.css).Length
3296
PS U:\>

■おまけ

 文字列を直接書いた場合は、メンバーを呼び出すのに( )が不要です。

PS U:\> "abcdefg".Length
7
PS U:\>

 文字列に限らず、メソッドやプロパティによって取得したオブジェクトを使う場合も( )が不要です。

PS U:\> "abcdefg".Replace("cde","01234").Length
9
PS U:\> "abcdefg".Replace("cde","01234")  # 比較用に実行
ab01234fg
PS U:\>
web拍手 by FC2
posted by 北条利彦 at 00:18 | Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2010年09月21日

PowerShell 逆引きリファレンス

 最近、PowerShellの記事をたくさん書いていたのですが、このまま続けるとこのブログがPowerShellの記事で埋もれてしまいそうなので、別のサイトを作りました。

PowerShell逆引きリファレンス
http://powershell.web.fc2.com/
人気ブログランキング

 このサイトを作るために使ったソフトの都合で、あまり順序よく記事が並んでいないのですが、ブログに書くよりはまだいいかなと思います(閲覧するのにも記事を書くのにも)。

 逆引きリファレンスという割には記事が少ないですが、早めに増やしていこうと思います。
web拍手 by FC2
posted by 北条利彦 at 23:31 | Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2010年09月10日

[PowerShell] 例外を補足する

 PowerShell には例外補足機能がついていて、trap { } と記述します。trapは、C#に出てくるようなtry 〜 catch 文とは異なっていて、なかなか特徴的な動作をします。
人気ブログランキング

trapを書く位置


 スクリプトブロックのどかこにtrapを書くと、trapを書いた位置より後ろ(↓)で発生した例外はもちろん補足しますが、前で発生した例外も補足します。

■TrapTest1.ps1
1/$null

trap
{
  Write-Host "例外を補足しました。"
  exit
}

■実行結果
PS U:\> .\Scripts\TrapTest.ps1
例外を補足しました。
PS U:\>

 例外は1/$nullのところで発生しますが、その下に書いたtrapで例外を補足しました。

スクリプトブロック内で発生した例外


 trap内でcontinueと記述すると、trapブロックに書かれた処理を実行したのち、例外が発生した次の行からスクリプトが再開されます。

 ただし、スクリプトブロック内で例外が発生しした場合は、trapが発生したスクリプトブロックのレベルで考えた次の命令を実行します。次の場合、trapはifの外に書かれているため、ifのスクリプトブロックが1つの文とみなされ、ifの次の文からスクリプトを実行します。

■TrapTest2.ps1
Write-Host "  実行A"
if($true)
{
  Write-Host "  実行B"
  1/$null
  Write-Host "  実行C"
}
Write-Host "  実行D"

trap
{
  Write-Host "  例外を補足しました。"
  continue
}

■実行結果
PS U:\> .\Scripts\TrapTest2.ps1
  実行A
  実行B
  例外を補足しました。
  実行D
PS U:\>

 ifは{ }全体で1つの文と考えられるため、「実行C」とは表示されませんでした。

一度補足した例外を再度補足する


 trapで補足した際、breakと記述すると、breakのところで再び例外が発生したかのような動作をします。さらに、break文で発生した(ように見える)例外を、他のtrapで補足することができます。

■TrapTest3.ps1
Write-Host "  実行A"
if($true)
{
  Write-Host "  実行B"
  1/$null
  Write-Host "  実行C"
  trap
  {
    Write-Host "  例外[1]を補足しました。"
    break
  }
}
Write-Host "  実行D"

trap
{
  Write-Host "  例外[2]を補足しました。"
  continue
}

■実行結果
PS U:\> .\Scripts\TrapTest3.ps1
  実行A
  実行B
  例外[1]を補足しました。
  例外[2]を補足しました。
  実行D
PS U:\>

 if内のtrapでbreakが実行されたので、その外側のスクリプトブロックに書かれているtrapで例外を補足しました。

trapの中で変更された値


 trapのスクリプトブロック内で変数を使用した場合、その変数は、スクリプトブロックの外の変数をコピーした別の変数であるかのように振る舞います。

 したがって、trapのスクリプトブロック内で値を変更した場合、スクリプトブロックの外の変数には影響が及びません。

 ただし、参照型変数の参照先であるオブジェクトを変更した場合は、スクリプトブロックの外にも影響が及びます。

■TrapTest4.ps1
$obj=New-Object PSObject
$obj | Add-Member NoteProperty Value 0

$x=1; $obj.Value=1
Write-Host ("  [A1] x={0}, o.V={1}" -f $x,$obj.Value)
1/$null
Write-Host ("  [A2] x={0}, o.V={1}" -f $x,$obj.Value)

trap
{
  Write-Host ("  [B1] x={0}, o.V={1}" -f $x,$obj.Value)
  $x=2; $obj.Value=2
  Write-Host ("  [B2] x={0}, o.V={1}" -f $x,$obj.Value)
  continue
}

■実行結果
PS U:\> .\Scripts\TrapTest.ps1
  [A1] x=1, o.V=1
  [B1] x=1, o.V=1
  [B2] x=2, o.V=2
  [A2] x=1, o.V=2
PS U:\>

 trap{ }内の$xは値型変数なので、trap内で$xを変更してもtrapの外$xの値は変わりませんでした。$objは参照型変数なのでtrap内の変更がtrapの外にも影響しました。

 この違いをファイル操作にたとえると、ファイル本体をコピーした場合とショートカットをコピーした場合の違いのようなものです。

 値型変数(この場合は$x)はファイル本体がコピーされるようなものなので、コピー先を変更してもコピー元には影響しません。

 参照型変数(同じく$obj)はショートカットをコピーするようなもので、コピー先ショートカットからファイルを開いて内容を変更すると、コピー元のショートカットからファイルを開いても更新された内容が見えます。
web拍手 by FC2
タグ:TRAP
posted by 北条利彦 at 22:54 | Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2010年09月09日

[PowerShell] .NET Frameworkでパスやファイルの処理をするときの注意事項

 PowerShellで扱っているカレントディレクトリと、.NET Frameworkのカレントディレクトリは一般に異なります。
人気ブログランキング
PS U:\Work> [IO.Directory]::GetCurrentDirectory()
C:\Users\Hojo
PS U:\Work>

 そのため、PowerShellを通して.NET Frameworkの入出力処理をしようとするとパスを誤認してしまいます。下記の例では、PowerShellのカレントディレクトリがU:\Workであるのに、 .NETではC:\Users\Hojoを探してしまっています。

PS U:\Work> [IO.File]::OpenRead('.\TestFile.txt')
"1" 個の引数を指定して "OpenRead" を呼び出し中に例外が発生しました: "ファイル 'C:\Users\Hojo\TestFile.txt' が見つかりませんでした。"
発生場所 行:1 文字:20
+ [IO.File]::OpenRead <<<< ('.\TestFile.txt')
    + CategoryInfo          : NotSpecified: (:) []、MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

PS U:\Work>

 PowerShellを通して.NET Frameworkの相対パス処理を行うには、まず.NET Frameworkのカレントディレクトリを変更します。

PS U:\Work> [IO.Directory]::SetCurrentDirectory(
>> (Get-Location).Path)
>>
PS U:\Work> [IO.Directory]::GetCurrentDirectory()
U:\Work
PS U:\Work>
web拍手 by FC2
posted by 北条利彦 at 07:23 | Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2010年09月08日

[PowerShell] フォルダー一覧・ファイル一覧をそれぞれ取得する

 PowerShellで、指定フォルダー直下のファイルやフォルダーの一覧を取得するには Get-ChildItem コマンドレットを使用します。
人気ブログランキング
PS U:\work\Test> Get-ChildItem


    ディレクトリ: U:\work\Test


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----        2010/09/08      6:52            Folder1
d----        2010/09/08      6:52            Folder2
-a---        2010/09/08      6:53       9069 File1.txt
-a---        2010/09/08      6:54       9236 File2.txt


PS U:\work\Test>

 Get-ChildItemコマンドレットは、フォルダーとファイルの両方を出力します。フォルダーのみ、ファイルのみを出力してほしい場合、Where-Objectコマンドレット( ?{...} )とTest-Path コマンドレットを使用するとうまくいきます。

PS U:\work\Test> # フォルダー一覧
PS U:\work\Test> Get-ChildItem | ?{ Test-Path $_ -PathType Container }


    ディレクトリ: U:\work\Test


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----        2010/09/08      6:52            Folder1
d----        2010/09/08      6:52            Folder2


PS U:\work\Test> # ファイル一覧
PS U:\work\Test> Get-ChildItem | ?{ Test-Path $_ -PathType Leaf }


    ディレクトリ: U:\work\Test


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2010/09/08      6:53       9069 File1.txt
-a---        2010/09/08      6:54       9236 File2.txt


PS U:\work\Test>

 出力結果は省略しますが、.NET Frameworkのメソッドを利用して、型名を判定する方法も使用できます。

PS U:\work\Test> Get-ChildItem | ?{ $_.GetType().Name -eq "DirectoryInfo" }
PS U:\work\Test> Get-ChildItem | ?{ $_.GetType().Name -eq "FileInfo" }

 次の方法は@ITに掲載されているものですが、フォルダーによってはうまくいきません。

PS U:\work\Test> Get-ChildItem | ?{ $_.Attributes -eq "Directory" }
PS U:\work\Test> # 何も表示されない。

 これでうまく実行できるフォルダーもあるのですが、たとえばWindowsフォルダーはうまく表示されませんでした。なぜなら、(私の環境では)WindowsフォルダーのAttributes値が"Directory"と等しくないためです。

PS U:\work\Test> (Get-Item 'C:\Windows').Attributes
Directory, NotContentIndexed
PS U:\work\Test> (Get-Item 'C:\Windows').Attributes -eq "Directory"
False
PS U:\work\Test>

 「Attributesが"Directory"と等しい」という条件では駄目だったので、「Attributesが"Directory"を含む」という条件ではどうか確認してみます。

PS U:\work\Test> (Get-Item 'C:\Windows').Attributes -contains "Directory"
False
PS U:\work\Test>

 やっぱり駄目でした。……というのは、Attributesは配列でないためです。enum型なのです。

PS U:\work\Test> (Get-Item 'C:\Windows').Attributes.GetType()

IsPublic IsSerial Name              BaseType
-------- -------- ----              --------
True     True     FileAttributes    System.Enum

 enum型の値は、複数の値についてビットごとのORをとったものです。したがって、ビットごとのANDを計算すれば、AttributesにDirectoryが含まれているかどうかが分かります。

PS U:\work\Test> (Get-Item 'C:\Windows').Attributes -band [System.IO.FileAttributes]::Directory
16
PS U:\work\Test> (Get-Item 'C:\Windows\_default.pif').Attributes -band [System.IO.FileAttributes]::Directory
0

 フォルダーと比較すると結果が16になり、適当なファイルも実験してみたら結果が0になったので、どうやらフォルダーとファイルの区別ができたようです。「16」という値が分かれば、あとはこれで大丈夫です。

PS U:\work\Test> (Get-Item 'C:\Windows').Attributes -band 16
16

 うまくいきました。しかしこの方法を使用すると、何の脈絡もなく「16」という数値が出てくることになります。これをたとえばPowerShellスクリプトに書いたりすると、あとで読んだときに意味が分からなくなりますので、この方法を使用するのは望ましくありません。
web拍手 by FC2
posted by 北条利彦 at 20:26 | Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2010年08月24日

[PowerShell] 16進のダンプ表示を行う

 以下のコードをスクリプトとして保存すると、16進数のダンプができます。スイッチパラメーターによって、以下の設定ができます。
スイッチ意味
-NoSeparatorバイトごとの空白区切りを非表示。
-NoHeader最初の行の0〜Fを非表示。
-NoLineNumber行番号を非表示。
人気ブログランキング

スクリプト(Dump.ps1)

param
(
  [switch]$NoSeparator,
  [switch]$NoHeader,
  [switch]$NoLineNumber,
 
  [Parameter(Mandatory=$true)]
  [string]
  $LiteralPath
)

# ヘッダー表示

if(-not $NoHeader)`
{
  $builder=New-Object System.Text.StringBuilder
  if(-not $NoLineNumber)
  {
    $builder.Append("       ")> $null
  }
 
  for($i=0; $i -lt 16; $i++)`
  {
    $builder.Append($i.ToString("X")) > $null
    $builder.Append(' ') > $null
    if(-not $NoSeparator)
    {
      $builder.Append(' ') > $null
    }
  }
  $builder.ToString()
}

# ダンプ表示

$byteCount=0
$lineIndex=0
$lineIndexLength=6
$bodyBuilder=New-Object System.Text.StringBuilder

function Output([int]$lineIndex)
{
  $indexText=""
  $bodyText=$bodyBuilder.ToString()
  if(-not $NoLineNumber)
  {
    $indexText=$lineIndex.ToString(
      "X").PadLeft($lineIndexLength,'0')+" "
  }
  $bodyBuilder.Length=0
  $indexText+$bodyText
}

Get-Content $LiteralPath -Encoding byte |
  %{
    $bodyBuilder.Append(
      $_.ToString("X").PadLeft(2,'0')) > $null
    if(-not $NoSeparator)
    {
      $bodyBuilder.Append(' ') > $null
    }
   
    $byteCount=($byteCount+1)%16
   
    if($byteCount -eq 0)
    {
      Output $lineIndex
      $lineIndex++
    }
   }

if($byteCount -ne 0) { Output }

使用例

> .\Dump.ps1 .\Test.txt
       0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
000000 61 31 20 62 31 20 63 31 0D 0A 61 32 20 62 32 20
000001 63 32 0D 0A 61 32 20 62 32 20 63 33 0D 0A 61 33
000002 20 62 33 20 63 33
>
> .\Dump.ps1 .\Test.txt -NoHeader -NoLineNumber -NoSeparator
61312062312063310D0A613220623220
63320D0A61322062322063330D0A6133
206233206333
web拍手 by FC2
posted by 北条利彦 at 00:10 | Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2010年08月23日

[PowerShell] 正規表現を使う

 PowerShellで正規表現を使用する例です。特にコマンドを使用せずとも、.NET Frameworkの呼び出しだけで実現できます。
人気ブログランキング
> $text='1:abc 2:def 3:ghi 4:jkl'
> $pattern='(?<=2:)[^\s]*'
>
> #マッチするかどうか(その1)
> [regex]::IsMatch($text,$pattern)
True
>
> #マッチするかどうか(その2)
> $text -match $pattern
True
>
> #マッチ結果の配列を作る
> $matches=[regex]::Matches($text,$pattern)
>
> #マッチした件数取得
> $matches.Count
1
> #マッチした最初のデータ
> $matches[0].Value
def
web拍手 by FC2
posted by 北条利彦 at 00:09 | Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2010年08月22日

[PowerShell] 指定した整数値を表す16進文字列を取得する(10進数→16進数変換)

 指定した整数$nを表す16進数文字列を取得します。.NET Frameworkのメソッドを使用すると簡単に実現できます。
人気ブログランキング
> $x=15*256+129
> $x.ToString("X")
F81
web拍手 by FC2
posted by 北条利彦 at 17:02 | Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2010年08月13日

[PowerShell] パスが通るファイルのフルパスを求める。

 PowerShell上でパスが通るものの、実体がどのフォルダー配下にあるのか分からず、ファイルの場所を特定したいときがあります。まずは、セミコロンで区切られているパスの文字列を配列にします。
人気ブログランキング
> $paths=$Env:Path.Split(';')
> $paths
%SystemRoot%\system32\WindowsPowerShell\v1.0\
C:\Windows\system32
C:\Windows
C:\Windows\System32\Wbem
C:\Windows\System32\WindowsPowerShell\v1.0\
>
 ファイルの存在確認にはTest-Pathコマンドレットが使用できます。これは一パラメーターを1つもち、そのパラメーターにはファイルのパスを指定すれば存在の有無を返してくれます。それを試す前に、引数に指定する文字列をちゃんと作れるかどうか確かめてみます。
> $paths[0]+"\mspaint.exe"
%SystemRoot%\system32\WindowsPowerShell\v1.0\\mspaint.exe
 フォルダー名とファイル名の間に「\」の記号が2個ついています。片方は不要ですが、2つ連続しても問題ない上、さきほどの$pathsの末尾には「\」がついていないものがありました。そのため、このままでいきましょう。

 あとは、$pathsに含まれているすべての要素それぞれに対し、Test-Pathコマンドレットを実行して、ファイルの存在確認をします。Where-Objectを使うと、パイプラインを流れる配列の要素の中から、条件に一致する要素だけを出力してくれるのでこれを併用します。下記の例ではWhere-Objectの略記である ? を使用しています。
> $paths | ?{ Test-Path ($_+"\mspaint.exe") }
C:\Windows\system32\mspaint.exe
>
 いろいろとやりましたが、結局、必要なのはこれだけです。
> $paths=$Env:Path.Split(';')
> $paths | ?{ Test-Path ($_+"\mspaint.exe") }
web拍手 by FC2
タグ:PowerShell PATH
posted by 北条利彦 at 01:06 | Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2010年08月11日

[PowerShell] 現在のフォルダー配下にあるテキストファイルの合計行数を求める。

 下記のコマンドを実行すると行数の合計を表示します。この例では、ファイル名の末尾が".cs"であり、かつ末尾が".designer.cs"でないファイルの合計行数を算出します。$judgeFunc={ ... }の中身を編集すれば、別の条件でファイルを絞り込むことができます。
人気ブログランキング
$judgeFunc={$args[0].EndsWith(".cs") -and -not $args[0].ToLower().EndsWith(".designer.cs")}

$files=(ls -Recurse | ?{ $_.GetType().Name -eq "FileInfo" } | %{ $_.FullName })
$selectedFiles=($files | ?{ & $judgeFunc $_ })
$lineCounts=($selectedFiles | %{ (cat $_).Count } )
$sum=0; $lineCounts | %{ $sum+=$_ }; $sum
web拍手 by FC2
posted by 北条利彦 at 23:40 | Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2010年04月27日

PowerShellスクリプトを起動

 当ブログでは初心者向けに解説記事を書いてきましたが、ネタが出てこないことが多いので、管理者向け、開発者向けの記事もこれからは書いていこうかと思います。かなり初歩的な話から、プロでも役立つ(かも知れない)話まで、レベルがばらばらになりそうですが、ご容赦下さい。

 さて、上級編の1件目はPowerShellです。PowerShellでは .ps1 の拡張子をつけたスクリプトを使用できますが、これをダブルクリックしてもそのままでは起動できません。いちいちウインドーを開いてコマンドを打つのは面倒なこともあるので、ショートカットに登録してみましょう。

 ショートカットを作る前に、[ファイル名を指定して実行]からPowerShellスクリプトを動かす実験です。単純に考えると、これで実行できそうです。

コマンド
PowerShell "D:\Test Script\Test.ps1"
実行結果
……?

 しかし、何やら赤い文字が書かれている画面が出てきたかと思ったら、すぐに消えてしまいます。人によっては想像がつくかもしれませんが、スクリプトの実行が終わるとすぐにウインドーを閉じてしまうため、そうなります。

 次のようにすると、スクリプトの実行が終わっても画面をそのまま残してくれます。

コマンド
PowerShell -noexit "D:\Test Script\Test.ps1"
実行結果
PsError.png

 さて、表示されているエラーが読めるようになりました。

 どうやら、スクリプトの位置を"D:\Test"だと勘違いしているようです。ちょうど半角空白の位置から後ろが無視されてしまっているので、半角空白のところが問題なのでしょう。上記のコマンドでは、どうやら二重引用符("")の内側をPowerShellのコマンドだと認識されるようです。ということは、半角空白の部分をエスケープすれば解決しそうです。

コマンド
PowerShell -noexit "D:\Test` Script\Test.ps1"
実行結果
PsOk.png

 ようやく実行できるようになりました。

 あとは、ショートカットを作成するだけです。デスクトップやフォルダーの何もないところで右クリック→[新規作成]→[ショートカット]をクリックすると、[ショートカットの作成]ダイアログが開きます。あとは、先ほどのコマンドを入力し、画面に従って進んでいくと作成されます。

人気ブログランキング


 なお、初期状態ではそもそもスクリプトを実行できないようになっているので、一度は次のコマンドを実行して下さい。

> Set-ExecutionPolicy RemoteSigned
web拍手 by FC2
posted by 北条利彦 at 20:35 | Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

広告


この広告は60日以上更新がないブログに表示がされております。

以下のいずれかの方法で非表示にすることが可能です。

・記事の投稿、編集をおこなう
・マイブログの【設定】 > 【広告設定】 より、「60日間更新が無い場合」 の 「広告を表示しない」にチェックを入れて保存する。


×

この広告は1年以上新しい記事の投稿がないブログに表示されております。