10

I need to have a timeout in powershell code where I'm running batch file, in case the batch file runs for a longer time or gets stuck. I also have a timeout in the batch script timeout 300> nul from which I seem to be getting this error and it is just skipping through the timeout and executing next lines. I do not get this error if I remove the timeout from batch script. But I need timeouts at both places, how do I resolve this ? Error- ERROR: Input redirection is not supported, exiting the process immediately.

PS Code-

$bs={
cd D:\files\ 
cmd.exe /c "mybatchfile.bat"
}
$timeoutseconds=800
$j=Start-Job -Scriptblock $bs
if(wait-Job $j -Timeout $timeoutseconds) {Receive-Job $j}
Remove-Job -force $j

batch script is something like this

cmd1
cmd2
timeout 300> nul
cmd3
4
  • 1
    First of all you should use %SystemRoot%\System32\timeout.exe /T 300 /NoBreak 1>NUL. Second, it would certainly help if you actually show us the commands you've replaced with cmd1, cmd2 and cmd3. Additionally I do not understand why you're changing directory using the cd alias as opposed to Set-Location or one of its less confusing aliases, sl, for instance. Commented Dec 18, 2022 at 18:35
  • It is likely that at least cmd2 would need to be start "" "cmd2" or similar. Commented Dec 18, 2022 at 19:14
  • @Compo actually I will be modifying this to run a remote conputer so I used cd to navigate to the dir, I will check out Set-Location. The command in batch script are for service restarting and killing some tasks so I am putting a timeout in between the steps. Commented Dec 19, 2022 at 5:49
  • As an aside: There's no need to use cmd /c in order to execute a batch file - simply invoke it directly, though note that PowerShell - by security-minded design - requires prefix .\ in order to execute a batch file in the current directory, in order to signal the intent to target an executable there explicitly. Thus, replace cmd.exe /c "mybatchfile.bat" with .\mybatchfile.bat Commented Nov 18, 2023 at 3:53

4 Answers 4

7

As should be obvious by now, the timeout command does not work, because it was developed within a mere 30 years. Maybe Microsoft will get it right after a couple of more decades.

In the mean time, everyone has been using, is using, and will be using, the following command:

    ping -n 2 -w 1000 localhost > nul

This command:

  • pings localhost two times.
  • waits 1000 milliseconds between the two pings.
  • swallows any messages produced by ping.

The two pings are necessary, because if you were to ping just once, the command would end instantaneously. Still, each ping to localhost is practically instantaneous, so the command will be running for roughly 1000 milliseconds.

Sign up to request clarification or add additional context in comments.

Comments

6
  • Whenever timeout.exe detects that its stdin input stream is redirected (not attached to a console), it aborts with the error message you saw.

  • Because of how background jobs launched with Start-Job are implemented, whatever child processes you run from the job's script block invariably see stdin as redirected, so you won't be able to call timeout.exe directly.[1]

There are workarounds:

  • Preferably, use thread-based background jobs, where this problem doesn't arise:

    • Start-ThreadJob runs your script block in a separate thread in the same process, and stdin redirection isn't involved; also, because no new PowerShell (child) process must be started, thread jobs are faster to create and require fewer resources.

      • Start-ThreadJob is part of the ThreadJob module, which comes with PowerShell (Core) 7+, and can be installed on demand in Windows PowerShell (e.g., with Install-Module -Scope CurrentUser ThreadJob)
    • Since Start-ThreadJob also integrates with PowerShell's job-management infrastructure and shares the core parameter syntax with Start-Job, all that should be necessary is to replace Start-Job with Start-ThreadJob in your code.

  • If you are able to modify the batch file you're calling, use a dummy ping.exe call in lieu of timeout.exe, which in effect emulates the latter's behavior.

  • Otherwise, call [the batch file that calls] timeout.exe via cmd.exe's internal start command:

    • However, this requires calling start without /B in order to execute the timeout.exe in a new window, whose stdin input by definition isn't redirected.

    • In turn, because start lacks a way to hide such a window, the closest you can come is to use /MIN in order to minimize it; a minimal example:

      Start-Job {
        # Simulate calling a batch file (cmd /c)
        # inside of which the `start` call is made,
        # waiting for 5 seconds in this example.
        cmd /c start /min timeout.exe 5
      } | Receive-Job -Wait -AutoRemoveJob
      

[1] Unless there is a way to reattach stdin to the console in a timeout.exe call - I'm not aware of such a feature, however (<CON creates an Access denied error).

2 Comments

Mixing Start-ThreadJob with timeout seems like it could be redundant in a world that has Wait-Event?
@RussellHorwood: The premise of the question that a preexisting batch file that happens to contain a timeout.exe call must be run from a PowerShell job. You're thinking of a different, possibly complementary use case, where you wait a fixed amount of time outside of the job (as also shown in the question).
3

You might be interested to add a delay in another way. One alternative is to wait with:

REM # waits a delay before refresh status of service
ping localhost

Indeed I had similar remote PowerShell issue in a scenario with a GitLab agent install script on remote server. By replacing timeout with ping, the script does not finish anymore with an error.

2 Comments

So how do you control the length of this delay? Note that the initial post tries to use 300 seconds - i.e. 5 minutes. By contrast, it looks like your solution is hard-coded to 3 seconds (plus change), due to ping.exe defaulting to 4 tries.
You can add parameters to ping to retry, number of count maybe - n 300
0

ping localhost works great but that is still 4 seconds

Just pipeline after:

powershell.exe -command "Start-Sleep 2" | [cmd script]

also try:

TIMEOUT /T [NUMBER] & [cmd script]

makes it more specific with time :-)

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.