tech.guitarrapc.cóm

Technical updates

PowerShell で プロセスの優先度(priority) を変更する

小ネタです。

ある場面において、プロセスの優先順位*1 を変更する機会があります。そんな時、良く紹介されているのは、 タスクマネージャーからの優先度の変更ですね。

image

しかし、長時間のプロセス、かつ処理ごとにプロセスが立ち上がり直す場合には、やってられません。ということで、PowerShellで変更する方法を見てみましょう。

プロセスの取得

まずは、プロセスを取得してみましょう。

Get-Process*2で見てみましょう。

PS> Get-Process -Name powershell | select *
# 省略
PriorityClass              : Normal

PriorityClass を設定する

単純に、タスクマネージャーで表示される優先度をセットすればokです。

$ps = Get-Process -Name powershell
$ps.PriorityClass = "High"

これで、優先度が変更されます。

image

PriorityClassはNoteProperty担っています。

PS> Get-Process -Name powershell | select PriorityClass | Get-Member
   TypeName: Selected.System.Diagnostics.Process

Name          MemberType   Definition
----          ----------   ----------
Equals        Method       bool Equals(System.Object obj)
GetHashCode   Method       int GetHashCode()
GetType       Method       type GetType()
ToString      Method       string ToString()
PriorityClass NoteProperty System.Diagnostics.ProcessPriorityClass PriorityClass=Normal

ご存知の通り、[System.Diagnostics.ProcessPriorityClass]はEnumです。

PS [System.Diagnostics.ProcessPriorityClass]
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     ProcessPriorityClass                     System.Enum

設定可能な定義を確認できます。

PS> [Enum]::GetNames([System.Diagnostics.ProcessPriorityClass])
Normal
Idle
High
RealTime
BelowNormal
AboveNormal

ここまでわかっていれば、さくっとファンクションにします。

コード

GitHubにおいておきます。

全文です。

function Set-ProcessPriorityClass
{
    [CmdletBinding()]
    param
    (
        [parameter(
            mandatory,
            position = 0,
            valueFromPipeline,
            valueFromPipelineByPropertyName)]
        [string[]]
        $Name,

        [parameter(
            mandatory = 0,
            position = 1,
            valueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [System.Diagnostics.ProcessPriorityClass]
        $Priority,

        [parameter(
            mandatory = 0,
            position = 2)]
        [switch]
        $WhatIf
    )

    begin
    {
        DATA message
        {
            ConvertFrom-StringData `
            "
                PriorityNotChange  = Process Name '{0}' Priority '{1}' was already same as Priority '{2}' you set. Skip priority change.
                PriorityChange     = Process Name '{0}' changed Priority from '{1}' to '{2}'.
                ProcessNotFound    = Process Name '{0}' not found. Skip priority change.
            "
        }
    }

    process
    {
        foreach ($n in $Name)
        {
            try
            {
                # Get process
                $ps = Get-Process | where Name -eq $n

                # process exist check
                if ($null -ne $ps)
                {
                    # what if check
                    if ($PSBoundParameters.WhatIf.IsPresent)
                    {
                        $Host.UI.WriteLine(("What if: " + $message.PriorityChange -f $ps.Name, $ps.PriorityClass, $Priority))
                    }
                    else
                    {
                        # process priority check
                        if ($ps.PriorityClass -ne $Priority)
                        {
                            # execute
                            Write-Verbose ($message.PriorityChange -f $ps.Name, $ps.PriorityClass, $Priority)
                            $ps.PriorityClass = $Priority
                        }
                        else
                        {
                            # priority want to change was same as current.
                            Write-Verbose ($message.PriorityNotChange -f $ps.Name, $ps.PriorityClass, $Priority)
                        }
                    }
                }
                else
                {
                    # process missing
                    Write-Warning ($message.ProcessNotFound -f $n)
                }
            }
            finally
            {
                # dispose item
                if ($ps -ne $null){$ps.Dispose()}
            }
        }
    }
}

利用方法

いわゆるPowerShellファンクションとして利用可能です。 Nameには、複数のプロセスをまとめて指定可能です。

パラメータ指定

パラメータを指定してもいいですね。 もちろんPriorityは、インテリセンスに候補もでます。

PS> Set-ProcessPriorityClass -Name "powershell" -Priority High -Verbose
VERBOSE: Process Name 'powershell' changed Priority from 'Normal' to 'High'.

パイプライン渡し

パイプラインからも渡せます。

PS> Get-Process | where Name -eq PowerShell | Set-ProcessPriorityClass -Priority Normal -Verbose
VERBOSE: Process Name 'powershell' changed Priority from 'High' to 'Normal'.

スプラッティング渡し

いわゆるSplatting = HashTableに定義して渡す手法も可能です。

PS> $param = @{Name = "powershell"; Priority = "High"}
PS> Set-ProcessPriorityClass @param -Verbose
VERBOSE: Process Name 'powershell' changed Priority from 'Normal' to 'High'.

WhatIf

-WhatIfスイッチを付けることで、実行しないでどうなるかも事前に確認可能です。*3

PS> Get-Process | where Name -eq PowerShell | Set-ProcessPriorityClass -Priority Normal -Verbose -WhatIf
What if: Process Name 'powershell' changed Priority from 'High' to 'Normal'.

まとめ

あとは好きなようにイベントを処理しましょう。

*1:いわゆるPriority

*2:Aliasはpsです。

*3:実行せずに確認のみです