PowerShell Start-Job Working Directory
-
19-09-2019 - |
Question
Is there a way to specify a working directory to the Start-Job command?
Use-case:
I'm in a directory, and I want to open a file using Emacs for editing. If I do this directly, it will block PowerShell until I close Emacs. But using Start-Job attempts to run Emacs from my home directory, thus having Emacs open a new file instead of the one I wanted.
I tried to specify the full path using $pwd, but variables in the script block are not resolved until they're executing in the Start-Job context. So some way to force resolving the variables in the shell context would also be an acceptable answer to this.
So, here's what I've tried, just for completeness:
Start-Job { emacs RandomFile.txt }
Start-Job { emacs "$pwd/RandomFile.txt" }
Solution
A possible solution would be to create a "kicker-script":
Start-Job -filepath .\emacs.ps1 -ArgumentList $workingdir, "RandomFile.txt"
Your script would look like this:
Set-Location $args[0]
emacs $args[1]
Hope this helps.
OTHER TIPS
There is nice solution from comments section of some dude's post. Commenter suggests to use Init
parameter to setup working directory of script block.
function start-jobhere([scriptblock]$block) {
Start-Job -Init ([ScriptBlock]::Create("Set-Location '$pwd'")) -Script $block
}
To summarize the problem:
A background job started with
Start-Job
does not inherit the current location (working directory).It defaults to
$HOME\Documents
in Windows PowerShell, and$HOME
in PowerShell Core (as of PSv5.1 / PSv6 alpha).If you're using PowerShell Core and you're targeting a file in the current directory, you can use the following, because the new
... &
syntax by default runs the job in the current directory:
emacs RandomFile.txt &
You cannot directly reference variables from the current session in a script block passed to
Start-Job
.
To complement jdmichal's own helpful answer and asavartsov's helpful answer, which still work well:
PSv3 introduced a simple way to reference the current session's variables in a script block that is executed in a different context (as a background job or on a remote machine), via the $using:
scope:
Start-Job { Set-Location $using:PWD; emacs RandomFile.txt }
Alternatively:
Start-Job { emacs $using:PWD/RandomFile.txt }
Note:
This GitHub issue proposes adding a
-WorkingDirectory
to theStart-Job
cmdlet.Start-Job -WorkingDirectory $PWD { emacs RandomFile.txt } # WISHFUL THINKING
This GitHub issue reports the surprising inability to use
$using:
in the script block passed to-InitializationScript
, even though it works in the main script block (the implied-ScriptBlock
parameter).Start-Job -Init { Set-Location $using:PWD } { emacs RandomFile.txt } # FAILS
try this
Start-Job -inputobject $pwd -scriptblock { emacs "$input/RandomFile.txt" }
Here $input
is predefined variable that internally take the value of -inputobject
parameter
Start-Job is an overkill for what you need (running a command in the background). Use Start-Process instead:
Start-Process -NoNewWindow emacs RandomFile.txt
There are no issue with the current directory in this approach. I have also created a function to make this as simple as possible:
function bg() {Start-Process -NoNewWindow @args}
and then the invocation becomes:
bg emacs RandomFile.txt
This works on Windows 7 (Powershell v2).
Just for completeness, here's the final script I implemented based on Filburt's answer, community-wiki style:
function Start-Emacs ( [string]$file )
{
Start-Job -ArgumentList "$pwd\$file" { emacs $args[0] }
}