Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 841
Slinky Avatar asked Apr 12 '13 11:04

Slinky


People also ask

How do I restrict a PowerShell script?

Set the PowerShell execution policy: To set the execution policy, use the Set-ExecutionPolicy cmdlet, and choose one of the following options: Restricted: this is the most restrictive option. Choosing this option won't allow configuration files to be loaded and scripts to run.

How do I stop a PowerShell script from running?

You can interrupt and stop a PowerShell command while it is running by pressing Control-C. A script can be stopped with the command exit. This will also close the PowerShell console.

How do I Run multiple PowerShell scripts at the same time?

To execute multiple commands in Windows PowerShell (a scripting language of Microsoft Windows), simply use a semicolon.


1 Answers

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
like image 136
Mr. Annoyed Avatar answered Sep 19 '22 06:09

Mr. Annoyed