I see many examples along the lines of:
Get-ChildItem -Filter "*.txt" | ForEach-Object { sajb {ren $_.fullname ($_.directoryname + "\" + "temp_" + $_.name + ".newext") } }
or what I think should be equivalent, using start-job -scriptblock:
Get-ChildItem -Filter "*.txt" | ForEach-Object { Start-Job -ScriptBlock {ren $_.fullname ($_.directoryname + "\" + "temp_" + $_.name + ".newext") } }
But these don't work for me. I get output like this and nothing happens to the files:
or this for my actual usecase:
If I remove the sajb block, then it works fine in series and does exactly what you'd expect. It's only when I try to run all commands in the loop in parallel that it fails.
The same operations do work fine from the Command prompt using:
for %x in ("*.txt") do (start "Convert" cmd /c "ren "%x" "temp_%x.newext"")
My purpose is to do this for some commands that run slowly in series but would run quickly in parallel using ffmpeg and sox, which also work fine in the for loop from the command prompt. I just can't get it working in PowerShell, even in the simple case like the file rename example above. What am I doing wrong with the start-job / sajb?
If it matters, I want to run this as a single PowerShell command from the PS prompt. I do not want to create a PowerShell script.
I see other posts with what look like similar questions, but I don't think they ever received functional answers, or if those answer are correct, I don't understand how to apply them to my situation:


sajbis simply a built-in alias of theStart-Jobcmdlet.Two asides:
The
Start-ThreadJobcmdlet offers a lightweight, much faster thread-based alternative to the child-process-based regular background jobs created withStart-Job. It comes with PowerShell (Core) 7+ and in Windows PowerShell can be installed on demand with, e.g.,Install-Module ThreadJob -Scope CurrentUser. In most cases, thread jobs are the better choice, both for performance and type fidelity - see the bottom section of this answer for why.In PowerShell (Core) 7+, the simplest solution is to use
ForEach-Objectwith the-Parallelparameter, which combines parallel execution with direct access to pipeline input via the automatic$_variable:As Santiago Squarzon notes, any
Start-Job(as well asStart-ThreadJob) call inside a (non-parallel)ForEach-Objectcall will not automatically see the automatic$_variable reflecting the current pipeline input object, given that it executes in a different runspace (in the case ofStart-Job, a runspace in a different process); therefore, you must reference / pass its value explicitly:Either: use
$using:_, via the$using:scope:$using:references requires enclosure in(...)- see GitHub issue #10876Or: pass any values to the job via the
-ArgumentList(-Args) parameter, which the job can then access via the automatic$argsvariable:Additionally, note that in Windows PowerShell (the legacy PowerShell edition whose latest and last version is 5.1),
Start-Jobscript blocks use a default working directory rather than inheriting the caller's - see the bottom section of this answer for details.