PowerShell Start-Job 工作目录

35

有没有办法在Start-Job命令中指定工作目录?

用例:

我在一个目录中,想要使用Emacs打开一个文件进行编辑。如果我直接这样做,它会阻塞PowerShell,直到我关闭Emacs。但是使用Start-Job尝试从我的主目录运行Emacs,因此Emacs会打开一个新文件而不是我想要的文件。

我尝试使用$pwd指定完整路径,但脚本块中的变量在执行于Start-Job上下文时才解析,因此一些强制在shell上下文中解析变量的方法也可以接受。

所以,为了完整起见,这里是我尝试过的:

Start-Job { emacs RandomFile.txt }
Start-Job { emacs "$pwd/RandomFile.txt" }
6个回答

37

这篇文章的评论区有一个好的解决方案 。评论者建议使用Init参数来设置脚本块的工作目录。

function start-jobhere([scriptblock]$block) {
    Start-Job -Init ([ScriptBlock]::Create("Set-Location '$pwd'")) -Script $block
}

1
++ 对于最通用的解决方案:不需要辅助脚本,也不需要后台脚本块需要知道和处理的输入参数。 - mklement0
"在处理路径中的空格时,应该在$pwd周围加上引号。" - ghostbust555
@ghostbust555 是正确的,需要使用单引号,因为这不是变量引用,而是字符串中的变量扩展。 - Sander
@Sander:确实(感谢您的纠正-我错过了正在构建的字符串)。然而,考虑到文件名可能包含'的情况,这有一点可能会出现问题,因此完全健壮的解决方案是:"Set-Location '$($pwd -replace "'", "''")'"。 - mklement0

17

更新:

PowerShell [Core] 7.0 带来了以下改进

  • 后台作业(包括线程作业和 ForEach-Object -Parallel)现在合理地继承调用者的当前(文件系统)位置。

  • 一个新的 -WorkingDirectory 参数允许您明确指定目录。


总结问题(适用于 Windows PowerShellPowerShell Core 6.x

  • 使用 Start-Job 启动的后台作业不会继承当前位置(工作目录)。

    • 在 Windows PowerShell 中,默认为 $HOME\Documents,在 PowerShell Core 中为 $HOME

    • 如果您正在使用 PowerShell Core 并且将目标设置为当前目录中的文件,则可以使用以下内容,因为默认情况下,新的 ... & 语法在当前目录中运行作业
      emacs RandomFile.txt &

  • 您无法直接从传递给 Start-Job 的脚本块中引用当前会话的变量。

为了补充 jdmichal 自己有用的答案asavartsov 有用的答案,它们仍然有效:

PSv3 引入了一种简单的方法,在执行不同上下文中的脚本块时(作为后台作业或在远程计算机上),通过 $using: 作用域引用当前会话的变量值。

Start-Job { Set-Location $using:PWD; emacs RandomFile.txt }

或者:

Start-Job { emacs $using:PWD/RandomFile.txt }

请参见about_Remote_Variables


注意:

  • 此 GitHub 问题报告了在传递给-InitializationScript的脚本块中无法使用$using:,尽管它在主脚本块(隐含的-ScriptBlock参数)中可以使用。
    • Start-Job -Init { Set-Location $using:PWD } { emacs RandomFile.txt } # 失败

16
一个可能的解决方案是创建一个“脚本启动器”:
Start-Job -filepath .\emacs.ps1 -ArgumentList $workingdir, "RandomFile.txt"
你的脚本应该像这样:
Set-Location $args[0]
emacs $args[1]
希望这可以帮到你。

1
这个非常成功。-ArgumentList 正是我正在寻找的东西。 - jdmichal
请记住,只有当$workingdir不包含空格或其他特殊字符时,此方法才有效。 - DiscoInfiltrator
@DiscoInfiltrator:在PowerShell中仅传递$workingdir很好用,即使有嵌入的空格和其他shell元字符(你可能正在考虑类POSIX外壳); 可以通过$dir ='C:\Program Files\';function foo(){set-location $ args [0]}; foo $dir进行验证。 - mklement0
@Filburt,你为什么称它为“kicker-script”? - Bowi
1
@Bowi 这只是个人命名问题 - 一个用于启动另一个脚本或可执行文件的脚本。 - Filburt

7

试一下这个

Start-Job -inputobject $pwd -scriptblock { emacs "$input/RandomFile.txt" }

这里的$input是预定义变量,内部获取了-inputobject参数的值。


5

对于你需要的任务(在后台运行命令),Start-Job 功能过于复杂。请使用 Start-Process 代替:

Start-Process -NoNewWindow emacs RandomFile.txt

在这种方法中,当前目录没有问题。我也创建了一个函数,使其尽可能简单:
function bg() {Start-Process -NoNewWindow @args}

然后调用变成了:

bg emacs RandomFile.txt

这适用于Windows 7(Powershell v2)。


但是 Start-Process 在终端关闭后仍然存在? - CMCDragonkai

2

为了完整性,这是我基于Filburt的答案实现的最终脚本,以社区维基风格呈现:

function Start-Emacs ( [string]$file )
{
    Start-Job -ArgumentList "$pwd\$file" { emacs $args[0] }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接