2010年10月30日土曜日

◆すべてのハードディスクからファイルを検索する

自PC全体からファイルを探したい時、個々のドライブ毎に検索するのは面倒。

まとめて検索するには以下のような感じで。

gwmi win32_logicaldisk -filter "drivetype=3" | %{dir ($_.DeviceID + "\") -filter target.txt -r}

2010年10月29日金曜日

◆スクリプトファイルのパスを取得する

同一ディレクトリーにあるファイルを前提にスクリプトを書くことはよくある。
しかし、スクリプトファイルのパスとカレントディレクトリーが一致するとは限らない。
そんな時、スクリプトファイルのパス(自分自身のパス)がほしくなる。
以下のようにして取得できるようだ。
Split-Path $myInvocation.MyCommand.path
ちなみに$myInvocationはコマンドラインからは値を返さない。

PowerShell: ◆スクリプトファイルのパスを取得する(補足)

Ver3からは「$PSCommandPath」や「$PSScriptRoot」で取得できるようになったようだ。

2010年10月27日水曜日

◆Bits転送4 GUIクライアント版

これまで調べてきたBits転送機能を使ってGUI版のクライアントを作ってみた。
画面はこんな感じ。
image

ソースはこんな感じ。(Bits.ps1)

Set-PSDebug -Strict
$host.UI.RawUI.WindowTitle = "Bits転送ホスト"

Import-Module BitsTransfer
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName Microsoft.VisualBasic
$bindingSource = [System.Windows.Forms.BindingSource]
$bindingSource1 = New-Object $bindingSource
$bindingSource2 = New-Object $bindingSource
$path = Split-Path $myInvocation.MyCommand.path
$MessageBox = [System.WIndows.Forms.MessageBox]
$StartedEndMark,$endedEndMark = "☆","★"
 
function Main{
   
    #Formソースの読み込み
    Write-Host "Bits転送起動中・・・"
    $source1 = "Form1.cs"
    $source2 = "Form1.Designer.cs"
    $source3 = "CommonClasses.cs"
    $source = gc (Join-Path $path $source1)
    $source += gc (Join-Path $path $source2)
    $source += gc (Join-Path $path $source3)
    Add-Type -TypeDefinition ($source | out-string) -ReferencedAssemblies System.Windows.Forms,
                                                                          System.Drawing,
                                                                          System.Data,
                                                                          System.Core
    #Form表示
    $form = New-Object Bits.Form1
    $iconPath =  Join-Path $path "prodicon.ico"
    if(Test-Path $iconPath){
        $icon = New-Object System.Drawing.Icon (Join-Path $path "prodicon.ico")
        $form.icon = $icon
    }

    #イベント登録
    $form.Add_Load({formLoad})                      #フォームLoad処理
    $form.Add_FormClosing({formClosing})            #フォームクローズ処理
   
    $form.btnSync.Add_Click({btnSyncClick})         #同期実行処理
   
    $form.btnSerial.Add_Click({btnSerialClick})     #直列キュー投入処理
    $form.btnSuspend.Add_Click({btnSuspendClick})   #サスペンド処理
    $form.btnResume.Add_Click({btnResumeClick})     #Resume処理
    $form.btnDelete.Add_Click({btnDeleteClick})     #削除処理
   
    $form.btnParallel.Add_Click({btnParallelClick}) #並列キュー投入処理
    $form.btnSuspend2.Add_Click({btnSuspend2Click}) #サスペンド処理
    $form.btnResume2.Add_Click({btnResume2Click})   #Resume処理
    $form.btnDelete2.Add_Click({btnDelete2Click})   #削除処理
   
    $form.foreground.Add_Click({contextGridPriorityClick("foreground")})  #コンテキストメニュー
    $form.High.Add_Click({contextGridPriorityClick("High")})  #コンテキストメニュー
    $form.Normal.Add_Click({contextGridPriorityClick("Normal")})  #コンテキストメニュー
    $form.Low.Add_Click({contextGridPriorityClick("Low")})  #コンテキストメニュー
   
    #データソース設定
    $form.DataGridView1.DataSource = $bindingSource1
    $form.DataGridView2.DataSource = $bindingSource2
   
    fncRegistTimer
   
    cls
    [Windows.Forms.Application]::Run($form)
}

#フォームLoad処理
function formLoad{
    #投入済みで未完了のJOBを回復
    Get-BitsTransfer | %{
        $job = New-Object Bits.Job
        $_.FileList | Set file
        $job.FileName = Split-Path $file.RemoteName -Leaf
        $job.FromText = $file.RemoteName
        $job.ToText = $file.LocalName
        $job.Status = $_.JobState
        $job.JobID = $_.JobId
        $job.EndMark = $StartedEndMark
        if($_.DisplayName -eq "Serial"){
            $bindingSource1.Add($job)
        }else{
            $bindingSource2.Add($job)
        }
    }
   
    #直列キューの未投入分を回復
    $xmlpath = Join-Path $path "bindingsource1.xml"
    if(Test-Path $xmlpath){
        Import-Clixml $xmlpath | %{
            $job = New-Object Bits.Job
            $job.FileName = $_.FileName
            $job.FromText = $_.FromText
            $job.ToText = $_.ToText
            $job.Status = ""
            $job.EndMark = $StartedEndMark
            $bindingSource1.Add($job)
        }
    }
   
    #GridViewを選択解除&表示行調整
    if($bindingSource1.Count -gt 0){
        $form.DataGridView1.FirstDisplayedScrollingRowIndex = $bindingSource1.Count - 1
        $form.DataGridView1.ClearSelection()
    }
    if($bindingSource2.Count -gt 0){
        $form.DataGridView2.FirstDisplayedScrollingRowIndex = $bindingSource2.Count - 1
        $form.DataGridView2.ClearSelection()
    }
}

#フォームClosing処理
function formClosing{
    #$_.Cancel = $true
    $message1 = @"
キューに未終了のJOBが存在します。
JOBはそのまま継続されますが、
再度アプリケーションを実行するまで転送は完了しません。
"@
   
    #直列キュー未投入分を退避
    $bindingSource1 | ?{$_.Status -eq ""} | Export-Clixml -Path (Join-Path $path "bindingsource1.xml")
   
    foreach($job in $bindingSource1){
        if($job.EndMark -eq $StartedEndMark){
            $MessageBox::Show($message1)
            return
        }
    }
   
    foreach($job in $bindingSource2){
        if($job.EndMark -eq $StartedEndMark){
            $MessageBox::Show($message1)
            return
        }
    }
}

#同期実行処理
function btnSyncClick{
    if(fncCheckPath){
        [Microsoft.VisualBasic.Interaction]::AppActivate("Bits転送ホスト")
        Start-BitsTransfer $form.txtFrom.text $form.txtTo.text
        [Microsoft.VisualBasic.Interaction]::AppActivate("Bits転送")
    }else{
        $MessageBox::Show("転送元と転送先に正しいパスを指定してください")
        Return
    }
}

#直列キュー投入処理
function btnSerialClick{
    if(fncCheckPath){
        $job = New-Object Bits.Job
        $job.FileName = Split-Path $form.txtFrom.text -Leaf
        $job.FromText = $form.txtFrom.text
        $job.ToText = $form.txtTo.text
        $job.Status = ""
        $job.EndMark = $StartedEndMark
        $bindingSource1.Add($job)
        $form.DataGridView1.ClearSelection()
        $form.DataGridView1.FirstDisplayedScrollingRowIndex = $bindingSource1.Count - 1
    }else{
        $MessageBox::Show("転送元と転送先に正しいパスを指定してください")
        Return
    }
   
}

#コンテキストメニュー処理(プライオリティ変更処理)
function contextGridPriorityClick($priority){
    $arrJobIds = @()
    foreach ($row in $form.dataGridView2.SelectedRows)
    {
        if($bindingSource2[$row.Index].EndMark -eq $StartedEndmark){
            $arrJobIds += $bindingSource2[$row.Index].JobID
        }
    }
   
    if($arrJobIds.Count){
        Get-BitsTransfer -JobId $arrJobIds | Set-BitsTransfer -Priority $priority
    }
}

#パス中の不要文字削除&空白チェック
function fncCheckPath{
    $form.txtFrom.text = $form.txtFrom.text -replace '"',""
    $form.txtTo.text = $form.txtTo.text -replace '"',""
    try{
        if($form.txtFrom.text.StartsWith("http")){
            $result = Test-Path $form.txtTo.text -ea Stop
            if($result){
                $form.txtTo.text = Resolve-Path $form.txtTo.text
            }
            return $result
        }else{
            $result =  (Test-Path $form.txtFrom.text -ea Stop) -and (Test-Path $form.txtTo.text -ea Stop)
            if($result){
                $form.txtFrom.text = Resolve-Path $form.txtFrom.text
                $form.txtTo.text = Resolve-Path $form.txtTo.text
            }
            return $result
        }
    }Catch{
        Write-Warning $error[0]
        return $false
    }
}

#サスペンド処理
function btnSuspendClick{
    fncSuspend $form.DataGridView1 $bindingSource1
}
function btnSuspend2Click{
    fncSuspend $form.DataGridView2 $bindingSource2
}
function fncSuspend($grid,$bindingSource){
    $selectedRows = $grid.SelectedRows
    if($selectedRows.Count){
        $selectedRows | %{
            $job = $bindingSource[$_.index] #選択行に対応するJOBオブジェクト
            if($job.EndMark -eq $StartedEndMark -and $job.JobId){
                Get-BitsTransfer -JobId $job.JobId | Suspend-BitsTransfer
            }
        }
    }else{
        $MessageBox::Show("対象の行(JOB)を選択してください")
    }
}

#Resume処理
function btnResumeClick{
    foreach($job in $bindingSource1){
        if(("Transferring","Connecting") -contains $job.Status){
            $MessageBox::Show("他のJOBが実行中です")
            return
        }
    }
   
    fncResume $form.DataGridView1 $bindingSource1 {break}
    
}
function btnResume2Click{  
    fncResume $form.DataGridView2 $bindingSource2 {continue}
}
function fncResume($grid,$bindingSource,$script){
    $selectedRows = $grid.SelectedRows
    if($selectedRows.Count){
        foreach($selectedRow in $selectedRows){
            $job = $bindingSource[$selectedRow.index]
            if($job.Status -eq "Suspended"){
                Get-BitsTransfer -JobId $job.JobId | Resume-BitsTransfer -Asynchronous
                Invoke-Command $script #直列キューの時はResumeはひとつだけ
            }
        }
    }else{
        $MessageBox::Show("対象の行(JOB)を選択してください")
    }
}

#削除処理
function btnDeleteClick{
    fncDelete $form.DataGridView1 $bindingSource1
}
function btnDelete2Click{
    fncDelete $form.DataGridView2 $bindingSource2
}
function fncDelete($grid,$bindingSource){
    $selectedRows = $grid.SelectedRows
    if($selectedRows.Count){
        $selectedRows | %{
            $job = $bindingSource[$_.index] #選択行に対応するJOBオブジェクト
            #TransferredはComplete-Transferで既に消えている
            if($job.EndMark -eq $StartedEndMark -and $job.JobId){ 
                Get-BitsTransfer -JobId $job.JobId | Remove-BitsTransfer
            }
            $bindingSource.RemoveAt($_.index)
            $grid.Refresh()
        }
    }else{
        $MessageBox::Show("対象の行(JOB)を選択してください")
    }
}

#並列キュー投入処理
function btnParallelClick{
    if(fncCheckPath){
        $job = New-Object Bits.Job
        #JOB情報を取得しGridへ表示
        $job.FromText = $form.txtFrom.text
        $job.ToText = $form.txtTo.text
        $job.EndMark = $StartedEndMark

        #フォームからプライオリティ判定
        foreach($cntrl in $form.groupBox2.Controls){
            if($cntrl -is [System.Windows.Forms.RadioButton]){
                if($cntrl.Checked){
                    $priority = $cntrl.Text
                    break
                }
            }
        }
           
        #Tranfertype判定
        $type = fncSetTransfertype
        $job.FileName = Split-Path $form.txtFrom.text -Leaf
       
        #JOB投入
        $ret = Start-BitsTransfer $form.txtFrom.text $form.txtTo.text  `
                                    -Async -TransferType $type -Priority $priority
        Set-BitsTransfer -BitsJob $ret -DisplayName "Parallel"
       
        #JOBステータス更新処理
        fncSetJobInfo $job $ret   

        $bindingSource2.Add($job)
        $form.DataGridView2.ClearSelection()
        $form.DataGridView2.FirstDisplayedScrollingRowIndex = $bindingSource2.Count -1
    }else{
        $MessageBox::Show("転送元と転送先に正しいパスを指定してください")
        Return
    }
   
}

#タイマー処理
function fncRegistTimer{

    $form.timer1.Add_Tick({
        #直列キューJOBステータス更新処理
        fncUpdateJobStatus $bindingSource1
       
        #並列キューJOBステータス更新処理
        fncUpdateJobStatus $bindingSource2
       
        #直列キューJOB投入処理
        :outer foreach($job in $bindingSource1){
            switch($job.Status){
                "Transferring"{break outer}
                "Connecting" {break outer}
                "" {
                        $type = fncSetTransfertype
                        $ret = Start-BitsTransfer $job.FromText $job.ToText  `
                                    -Async -TransferType $type -Priority Foreground
                        Set-BitsTransfer -BitsJob $ret -DisplayName "Serial"
                        fncSetJobInfo $job $ret
                        break outer
                    }
                default{}
            }
        }
       
        #データグリッドリフレッシュ
        $form.DataGridView1.AutoResizeColumns()
        $form.DataGridView1.Refresh()
        $form.DataGridView2.AutoResizeColumns()
        $form.DataGridView2.Refresh()

       
    })
}

function fncSetTransfertype{
    if($form.txtFrom.text.StartsWith("http")){
        return "Download"
    }elseif($form.txtTo.text.StartsWith("\\")){
        return "Upload"
    }else{
        return "Download"
    }
}

#JOBステータス更新処理
function fncUpdateJobStatus($bindingSource){
    foreach($job in $bindingSource){
        if($job.EndMark -eq $endedEndMark){continue}
        if($job.JobID){
            $ret = Get-BitsTransfer -JobId $job.JobID
            fncSetJobInfo $job $ret
        }              
    }
}

#JOBステータス設定処理
function fncSetJobInfo($job,$bitsInfo){
    $job.Status = $bitsInfo.JobState
    $job.JobID = $bitsInfo.JobId
    $job.StartTime = $bitsInfo.CreationTime
    $job.Priority = $bitsInfo.Priority
    $job.Percent = [int](($bitsInfo.BytesTransferred/$bitsInfo.BytesTotal)*100)
    if($job.Status -eq "Transferred"){
        $job.EndTime = $bitsInfo.TransferCompletionTime
        $job.EndMark = $endedEndMark
        $job.Percent = 100
        Complete-BitsTransfer -BitsJob $bitsInfo.JobId
    }
}

Main

さすがに面倒なのでFormはVisualStudioで作ってAdd-Typeでソースを取り込んでいる。
起動用のバッチファイルとかと纏めて以下へ置いた。
http://cid-ddbd2fc6fc634eaa.office.live.com/self.aspx/.Public/BitsGUI.zip

そこそこ使い物になるように思うのですが、いかがなものでしょう。

2010年10月23日土曜日

◆ログファイル(テキスト)の監視

Get-Contentコマンドレットに Wait パラメータを指定するだけでログファイル監視が出来る。

こんな感じ。
20101018163018

終了するときはCtrl + C で。

◆Word文書を纏めてHTMLに変換する

 

こんな感じかなぁ・・・。

$wdFormatHTML = 8
$iPath = "D:\Desktop\*"
$oPath = "D:\Desktop\test"
$ext = "docx"

$targetDocs = Get-ChildItem -Path $iPath -Include "*.$ext"
$word = new-object -comObject "Word.Application"
$word.Visible = $false

foreach($doc in $targetDocs){
    $word.Documents.Open($doc.fullname) | Out-Null
    $outFileName = (Join-Path $oPath $doc.name).Replace($ext,"html")
    $word.Application.ActiveDocument.SaveAs2([ref]$outFileName,[ref]$wdFormatHTML)
    $word.Application.ActiveDocument.Close()
}

$word.Quit()

2010年10月16日土曜日

◆CSVファイルを使ったファイルコピー

以下のような状態でそれぞれファイルをフォルダーの中に コピーする。
20101016225007

こんな感じ。

Copy-Item  -Path  D:\desktop\test1.txt  -Destination  D:\desktop\test1
Copy-Item  -Path  D:\desktop\test2.txt  -Destination  D:\desktop\test2

これと同じことをCSVファイルを使ってやってみる。
コピーするファイルとコピー先を指定したこんなファイルを用意しておく。
20101016225655

これを読み込んでCopy-Itemにパイプしてあげる。

Import-Csv  item.txt  |  Copy-Item

ファイル名やコピー先のフォルダー名に規則性がない場合は結構使い道があるかも。

2010年10月15日金曜日

◆PowershellでDBアクセス4

次はExcelシートをSQLで読み込んでみる。

接続文字列が若干違うだけ。

指定するSQLでは、シート名を From [Sheet1$] といった感じにする必要があるのでそのチェックを追加している。

param(
    [string]$dataSource  ,
    [string]$sqlCommand  ,
    [switch]${??}
)
$comment = @'
#####################################################

MRead-Excel.ps1
    Excelデータを読み込む。
param(
    [string]$dataSource  ,
    [string]$sqlCommand  ,
    [switch]${??}
)
例: 
    # Excelファイルにアクセスする
    MRead-Excel.ps1 (Resolve-Path xls_test.xls) -Sql 'Select * from [Sheet1$]'
#####################################################
'@
if(${??}) {$comment;return}
if(!$dataSource) {Write-Warning "Please specify a datasource." ; return}
if(!$sqlCommand) {Write-Warning "Please specify a query." ; return}

#シート名が正しく指定されていない場合はエラーを発生させる
if($sqlCommand -notmatch '\[.+\$\]')
{
    $error = ‘シート名はこんな感じで指定してね: [Sheet1$]'
    Write-Error $error
    return

#接続文字列を準備する
$connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=$dataSource;Extended Properties='Excel 12.0;';"

#データソースに接続し開く
$connection = New-Object System.Data.OleDb.OleDbConnection $connectionString
$command = New-Object System.Data.OleDb.OleDbCommand $sqlCommand,$connection
$connection.Open()

#結果をフェッチし、接続を閉じる
$adapter = New-Object System.Data.OleDb.OleDbDataAdapter $command
$dataset = New-Object System.Data.DataSet
[void]$adapter.Fill($dataset)
$connection.Close()

#クエリーからすべての行を返す
$dataset.Tables | Select-Object -ExpandProperty Rows | Out-GridView

◆PowershellでDBアクセス3

PowerShell: ◆PowershellでDBアクセスでは、Accessデータベースにアクセスしてみたので、今度はSQLServerにアクセスしてみる。

まぁ、接続文字列を変えるだけ。認証はWindows認証を使っている。

こんな感じ。

param(
    [string]$dataSource  ,
    [string]$dataBase  ,
    [string]$sqlCommand  ,
    [switch]${??}
)
$comment = @'
#####################################################

MRead-SQLServerDB.ps1
    SQLServerデータを読み込む。
param(
    [string]$dataSource  ,
    [string]$dataBase  ,
    [string]$sqlCommand  ,
    [switch]${??}
)
例: 
    # SQLServerにアクセスする
    MRead-SQLServer.ps1 -DataSource ".\sqlexpress" -DataBase "Northwind" -Sql "Select * from Orders"
#####################################################
'@
if(${??}) {$comment;return}
if(!$dataSource) {Write-Warning "Please specify a datasource." ; return}
if(!$sqlCommand) {Write-Warning "Please specify a query." ; return}

#接続文字列を準備する
$connectionString = "Provider=SqlOleDb;Data Source=$dataSource;Initial Catalog=$dataBase;Integrated Security=SSPI;"

#データソースに接続し開く
$connection = New-Object System.Data.OleDb.OleDbConnection $connectionString
$command = New-Object System.Data.OleDb.OleDbCommand $sqlCommand,$connection
$connection.Open()

#結果をフェッチし、接続を閉じる
$adapter = New-Object System.Data.OleDb.OleDbDataAdapter $command
$dataset = New-Object System.Data.DataSet
[void]$adapter.Fill($dataset)
$connection.Close()

#クエリーからすべての行を返す
$dataset.Tables | Select-Object -ExpandProperty Rows | Out-GridView

◆PowershellでDBアクセス2

Accessデータベースの読み込みに今度はDataReaderを使ってみる。

param(
    [string]$dataSource  ,
    [string]$sqlCommand  ,
    [switch]${??}
)
$comment = @'
#####################################################

MRead-AccessDB2.ps1
    Accessデータを読み込む。
param(
    [string]$dataSource  ,
    [string]$sqlCommand  ,
    [switch]${??}
)
例: 
    # Accesデータベースにアクセスする
    MRead-AccessDB2.ps1 "D:\Documents\NWind2007.accdb" -Sql "Select * from 社員"
#####################################################
'@
if(${??}) {$comment;return}
if(!$dataSource) {Write-Warning "Please specify a datasource." ; return}
if(!$sqlCommand) {Write-Warning "Please specify a query." ; return}

#接続文字列を準備する
$connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=$dataSource;"

#データソースに接続し開く
$connection = New-Object System.Data.OleDb.OleDbConnection $connectionString
$command = New-Object System.Data.OleDb.OleDbCommand $sqlCommand,$connection
$connection.Open()

#結果をフェッチし、接続を閉じる
$reader = $command.ExecuteReader()
while($reader.Read()){
    0..($reader.FieldCount - 1) | %{$reader.item($_)}
}
$reader.Close()
$connection.Close()


Datasetと違ってリッチなオブジェクトが帰ってこないのでリスト形式で表示している。
データ件数が大きい場合をのぞけばDatasetを使ったほうが便利っぽい。