tech.guitarrapc.cóm

Technical updates

PowerShellでファイル名をソートして最後のアイテムを取得するなら

このような記事があったのですが……ふむ…。 少し冗長なのでコードを自分なりに修正すると。

http://qiita.com/items/ad7c370f8659b8000ce9

元コード

元はこうです。

Get-ChildItem C:\hoge\fuga | Sort-Object -Descending | Select-Object -First 1 | Invoke-Item

準備

状況を考えるため、準備しましょう。 フォルダを作成します。

New-Item -Path C:\hoge\fuga\ -ItemType Directory

フォルダができました。

    ディレクトリ: C:\hoge

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----        2013/02/19     19:59            fuga

適当にファイルも作成します。

1..10 | %{$num=$_;"hoge" + $_ | Out-File C:\hoge\fuga\$num.txt}

出来てますね。

    ディレクトリ: C:\hoge\fuga

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2013/02/19     20:06         16 1.txt
-a---        2013/02/19     20:06         18 10.txt
-a---        2013/02/19     20:06         16 2.txt
-a---        2013/02/19     20:06         16 3.txt
-a---        2013/02/19     20:06         16 4.txt
-a---        2013/02/19     20:06         16 5.txt
-a---        2013/02/19     20:06         16 6.txt
-a---        2013/02/19     20:06         16 7.txt
-a---        2013/02/19     20:06         16 8.txt
-a---        2013/02/19     20:06         16 9.txt

元コードの実施

実行してみると?

Get-ChildItem C:\hoge\fuga | Sort-Object -Descending | Select-Object -First 1 | Invoke-Item

確かにファイル名ソートで最後のファイルが開きましたね。

元コードの改善

実際に開く直前でパイプを止めます。

Get-ChildItem C:\hoge\fuga | Sort-Object -Descending | Select-Object -First 1 #| Invoke-Item

最後を取っていますが…。

ディレクトリ: C:\hoge\fuga

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2013/02/19     20:06         16 9.txt

さて改善できないでしょうか。 もちろんできます。サンプルは「名前でソートを明示しつつ最後を取得」するのに、sort -Descendingselect -First 1がどうにも謎指定ですね。 よって、これで十分です。

Get-ChildItem C:\hoge\fuga | Sort-Object name | Select-Object -Last 1

同じ結果を取得できています。

ディレクトリ: C:\hoge\fuga

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2013/02/19     20:06         16 9.txt

最後のアイテムを実行したいなら、例の通りInvoke-Itemを付けるだけですね。

Get-ChildItem C:\hoge\fuga | Sort-Object name | Select-Object -Last 1 | Invoke-Item

Function化を意識する

また、Sort-Objectへのプロパティ指定もできているので、これならfunction化も容易です。 例えば、指定したパス(パス)を、指定したプロパティでソート($Sortby)して、指定個数取得($Count)というファンクションが思いつきます。 最後のアイテムを取得するGet-LastItemファンクション例

function Get-LastItem {

    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true)]
    [string]$Path,
    [Parameter(Mandatory=$true)]
    [string]$Sortby,
    [Parameter(Mandatory=$true)]
    [int]$Count
    )

    begin{
    }

    process{

        Get-ChildItem $Path | Sort-Object $SortBy | Select-Object -Last $Count

    }

    end{
    }

}

そう、これなら最初のアイテムを取得するGet-FirstItemファンクション例も簡単ですね。

function Get-LastItem {

    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true)]
    [string]$Path,
    [Parameter(Mandatory=$true)]
    [string]$Sortby,
    [Parameter(Mandatory=$true)]
    [int]$Count
    )

    begin{
    }

    process{

        Get-ChildItem $Path | Sort-Object $SortBy | Select-Object -Last $Count

    }

    end{
    }

}

例と同様の利用する時は以下のようにします。

Get-LastItem -Path C:\hoge\fuga -Sortby Name -Count 1

あるいは、取得するアイテム数を変えることもできます。

Get-LastItem -Path C:\hoge\fuga -Sortby Name -Count 3

あるいは-Sortbyを指定すれば、取得するアイテムソート対象をNameからCreationTimeやLastAccessTimeに変えることもできます。

Get-LastItem -Path C:\hoge\fuga -Sortby CreationTime -Count 1

Get-FirstItemファンクションは、Get-LastItemファンクションとは逆の動作ができますね。

Get-FirstItem -Path C:\hoge\fuga -Sortby Name -Count 1

1.txtが取れました。

    ディレクトリ: C:\hoge\fuga


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2013/02/19     20:06         16 1.txt

まとめ

この例で示したかったのは、以下の3点です。

  • Sort-Objectでのプロパティ指定は大事
  • Sort-Objectの並び順制御とSelect-Objectでの制御には無駄が出ないように気を付けて
  • 常にFunctionにすることを考えて書くといいかも

オレオレFunctionを作りましょう。

補足

牟田口先生からの指摘をいただきました。

記事タイトルから少しずれるのですが、本記事の内容は出力結果が1つではなく複数になった時、意識するといいです。例えば以下のテスト関数を書きます。

function Get-ItemTest {

    [CmdletBinding()]
    param(
      [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
      [string]$Path,
      [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
      [string]$Sortby,
      [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
      [int]$Count
    )

    process{
        Get-ChildItem $Path | Sort-Object $SortBy | Select-Object -First $Count | Format-Table -AutoSize
        Get-ChildItem $Path | Sort-Object $SortBy -Descending | Select-Object -First $Count | Format-Table -AutoSize
        Get-ChildItem $Path | Sort-Object $SortBy | Select-Object -Last $Count | Format-Table -AutoSize
        Get-ChildItem $Path | Sort-Object $SortBy -Descending | Select-Object -Last $Count | Format-Table -AutoSize
    }
}

実行してみましょう。

Get-ItemTest -Path C:\hoge\fuga -Sortby Name -Count 5

実行結果です。

PS C:\hoge\fuga> D:\Document\Program\Powershell\Get-FirstItem\Get-FirstItem.ps1


    ディレクトリ: C:\hoge\fuga


Mode         LastWriteTime Length Name
----         ------------- ------ ----
-a--- 2013/02/19     20:06     16 1.txt
-a--- 2013/02/19     20:06     18 10.txt
-a--- 2013/02/19     20:06     16 2.txt
-a--- 2013/02/19     20:06     16 3.txt
-a--- 2013/02/19     20:06     16 4.txt




    ディレクトリ: C:\hoge\fuga


Mode         LastWriteTime Length Name
----         ------------- ------ ----
-a--- 2013/02/19     20:06     16 9.txt
-a--- 2013/02/19     20:06     16 8.txt
-a--- 2013/02/19     20:06     16 7.txt
-a--- 2013/02/19     20:06     16 6.txt
-a--- 2013/02/19     20:06     16 5.txt




    ディレクトリ: C:\hoge\fuga


Mode         LastWriteTime Length Name
----         ------------- ------ ----
-a--- 2013/02/19     20:06     16 5.txt
-a--- 2013/02/19     20:06     16 6.txt
-a--- 2013/02/19     20:06     16 7.txt
-a--- 2013/02/19     20:06     16 8.txt
-a--- 2013/02/19     20:06     16 9.txt




    ディレクトリ: C:\hoge\fuga


Mode         LastWriteTime Length Name
----         ------------- ------ ----
-a--- 2013/02/19     20:06     16 4.txt
-a--- 2013/02/19     20:06     16 3.txt
-a--- 2013/02/19     20:06     16 2.txt
-a--- 2013/02/19     20:06     18 10.txt
-a--- 2013/02/19     20:06     16 1.txt

どうでしょうか? これが、この記事の意義だと考えています。 次回は、以下のようなfunctionへの値の渡し方について考えてみます。

@{
  path=(Get-Location).Path;
  key=((Get-ChildItem | Get-Member -MemberType Property).Name | ? {$_ -like "Na*"});
  count=5} | %{ Get-FirstItem -Path $_.path -Sortby $_.key -Count $_.count }

こんな風にでます。

    ディレクトリ: C:\hoge\fuga


Mode         LastWriteTime Length Name
----         ------------- ------ ----
-a--- 2013/02/19     20:06     16 1.txt
-a--- 2013/02/19     20:06     18 10.txt
-a--- 2013/02/19     20:06     16 2.txt
-a--- 2013/02/19     20:06     16 3.txt
-a--- 2013/02/19     20:06     16 4.txt