Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.9k views
in Technique[技术] by (71.8m points)

windows - Assure only 1 instance of PowerShell Script is Running at any given Time

I am writing a batch script in PowerShell v1 that will get scheduled to run let's say once every minute. Inevitably, there will come a time when the job needs more than 1 minute to complete and now we have two instances of the script running, and then possibly 3, etc...

I want to avoid this by having the script itself check if there is an instance of itself already running and if so, the script exits.

I've done this in other languages on Linux but never done this on Windows with PowerShell.

For example in PHP I can do something like:

exec("ps auxwww|grep mybatchscript.php|grep -v grep", $output);
if($output){exit;}

Is there anything like this in PowerShell v1? I haven't come across anything like this yet.

Out of these common patterns, which one makes the most sense with a PowerShell script running frequently?

  1. Lock File
  2. OS Task Scheduler
  3. Infinite loop with a sleep interval
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Here's my solution. It uses the commandline and process ID so there's nothing to create and track. and it doesn't care how you launched either instance of your script.

The following should just run as-is:

    Function Test-IfAlreadyRunning {
    <#
    .SYNOPSIS
        Kills CURRENT instance if this script already running.
    .DESCRIPTION
        Kills CURRENT instance if this script already running.
        Call this function VERY early in your script.
        If it sees itself already running, it exits.

        Uses WMI because any other methods because we need the commandline 
    .PARAMETER ScriptName
        Name of this script
        Use the following line *OUTSIDE* of this function to get it automatically
        $ScriptName = $MyInvocation.MyCommand.Name
    .EXAMPLE
        $ScriptName = $MyInvocation.MyCommand.Name
        Test-IfAlreadyRunning -ScriptName $ScriptName
    .NOTES
        $PID is a Built-in Variable for the current script''s Process ID number
    .LINK
    #>
        [CmdletBinding()]
        Param (
            [Parameter(Mandatory=$true)]
            [ValidateNotNullorEmpty()]
            [String]$ScriptName
        )
        #Get array of all powershell scripts currently running
        $PsScriptsRunning = get-wmiobject win32_process | where{$_.processname -eq 'powershell.exe'} | select-object commandline,ProcessId

        #Get name of current script
        #$ScriptName = $MyInvocation.MyCommand.Name #NO! This gets name of *THIS FUNCTION*

        #enumerate each element of array and compare
        ForEach ($PsCmdLine in $PsScriptsRunning){
            [Int32]$OtherPID = $PsCmdLine.ProcessId
            [String]$OtherCmdLine = $PsCmdLine.commandline
            #Are other instances of this script already running?
            If (($OtherCmdLine -match $ScriptName) -And ($OtherPID -ne $PID) ){
                Write-host "PID [$OtherPID] is already running this script [$ScriptName]"
                Write-host "Exiting this instance. (PID=[$PID])..."
                Start-Sleep -Second 7
                Exit
            }
        }
    } #Function Test-IfAlreadyRunning


    #Main
    #Get name of current script
    $ScriptName = $MyInvocation.MyCommand.Name 


    Test-IfAlreadyRunning -ScriptName $ScriptName
    write-host "(PID=[$PID]) This is the 1st and only instance allowed to run" #this only shows in one instance
    read-host 'Press ENTER to continue...'  # aka Pause

    #Put the rest of your script here

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...