Search code examples
powershellmultiprocess

Powershell exits on where cmdlet


I'm writing a script that should run two python apps and then cycle through solution files, searching for new and updated files with py and json extensions and restarting these apps every time any condition is fulfilled. I hit a wall at the point where I get file names. At this point (line 6), the second ps window just closes instantly even though two different commands should pause it and two more after, it just exits if there is where part on line 6, doing nothing.

$bot = start python .\source\run.py -WorkingDirectory .\bot -WindowStyle Hidden -PassThru # start bot app and save info about it
$web = start python .\source\run.py -WorkingDirectory .\web -WindowStyle Hidden -PassThru # start web app and save info about it
$cycle = start powershell { # start new ps window which will check if any file was changed 
    cmd /c pause # does nothing ???
    Read-Host # does nothing ???
    $fs = dir .\ -recurse | where { $_.extension -in ".py",".json" };
    cmd /c pause # does nothing ???
    Read-Host # does nothing ???
} -PassThru
echo Stop? # indicate script is ready to stop
Read-Host # wait for input before stopping
Stop-Process -Id $bot.Id # stop bot app
Stop-Process -Id $web.Id # stop web app
Stop-Process -Id $cycle.Id # stop second ps window

Solution

  • Your call to the Windows PowerShell CLI, powershell.exe, implicitly uses the -Command
    (-c) parameter
    .

    In order for embedded " characters in a command string to be recognized as such in a
    -Command argument, you must escape them as \"
    (sic) - otherwise, they effectively get removed during initial command-line parsing - see the bottom section of this answer for background information.
    Alternatively, you could use embedded '...' quoting, which requires no escaping.

    • Since you neglected to perform this escaping, the command became syntactically invalid, resulting in the new PowerShell window auto-closing almost instantaneously (after printing an error).
    • As a general troubleshooting tip, precede the command argument with -NoExit, which keeps the PowerShell session and therefore the window open, which allows you to see what error occurred.

    Also, while using a script block literal ({ ... }) is syntactically convenient, it can be be conceptually confusing, given that the positionally implied -ArgumentList (-Args) parameter of the Start-Process (start) you're passing the script block to is [string[]]-typed and therefore invariably converts arguments to strings. Therefore, consider using a (verbatim) here string instead.

    To put it all together (note that I'm using PowerShell's built-in pause function for simplicity, which is simply a Read-Host wrapper with the following code: $null = Read-Host 'Press Enter to continue...'):

    $bot = start python .\source\run.py -WorkingDirectory .\bot -WindowStyle Hidden -PassThru # start bot app and save info about it
    $web = start python .\source\run.py -WorkingDirectory .\web -WindowStyle Hidden -PassThru # start web app and save info about it
    
    # Note how " is escaped as \" 
    # Alternatively, use ' (while equivalent here, it isn't always)
    $cycle = start -PassThru powershell @'
        pause
        dir .\ -recurse | where { $_.extension -in \".py\", \".json\" }
        pause
    '@
    
    Read-Host Stop? # wait for input before stopping
    Stop-Process -Id $bot.Id, $web.Id, $cycle.Id
    

    Also note:

    • String Stop? is passed directly to Read-Host (where it implicitly binds to the -Prompt parameter), to make Read-Host itself print that string; however, note that : is invariably appended to the specified string.

    • A single Stop-Process call is sufficient, given that its -Id parameter accepts an array of PIDs (process IDs).