Make sure that only 1 PowerShell Script instance is running at any given time.

I am writing a script package in PowerShell v1 that will run on schedule, let it speak once a minute. Inevitably, the time will come when the work will take more than 1 minute, and now we have two instances of the script, and then, possibly 3, etc.

I want to avoid this if the script itself checks to see if the instance itself is already there, and if so, the script exits.

I did this in other languages ​​on Linux, but I never did it on Windows using 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 something similar in PowerShell v1? I have never seen anything like it.

Of these common patterns that are most commonly used when running a PowerShell script?

  • Lock file
  • OS Task Scheduler
+5
6

script powershell.exe -File, powershell, script, :

Get-WmiObject Win32_Process -Filter "Name='powershell.exe' AND CommandLine LIKE '%script.ps1%'"
+7

Powershell , . script -. , , V2.

$interval = 1
while ($true)
 {
  $now = get-date
  $next = (get-date).AddMinutes($interval)

  do-stuff

  if ((get-date) -lt $next)
    {
     start-sleep -Seconds (($next - (get-date)).Seconds)
    }
  }
+4

, , . . script , - , script, . script . , script .

+1

"" " " . , . , . ( ), script .

"":

If the task is already running, then the following rule applies: 
Do not start a new instace
+1

. , . , - script.

:

    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' 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
+1
$otherScriptInstances=get-wmiobject win32_process | where{$_.processname -eq 'powershell.exe' -and $_.ProcessId -ne $pid -and $_.commandline -match $($MyInvocation.MyCommand.Path)}
if ($otherScriptInstances -ne $null)
{
    "Already running"
    cmd /c pause
}else
{
    "Not yet running"
    cmd /c pause
}

,

$MyInvocation.MyCommand.Path (FullPathName)

$MyInvocation.MyCommand.Name (Scriptname)
0
source

All Articles