Рабочий каталог PowerShell Start-Job

Есть ли способ указать рабочий каталог команде Start-Job?

Пример использования:

Я нахожусь в каталоге и хочу открыть файл с помощью Emacs для редактирования. Если я сделаю это напрямую, PowerShell будет заблокирован до тех пор, пока я не закрою Emacs. Но использование Start-Job пытается запустить Emacs из моего домашнего каталога, таким образом заставляя Emacs открывать новый файл вместо того, который я хотел.

Я попытался указать полный путь с помощью $ pwd, но переменные в блоке сценария не разрешаются, пока они не выполняются в контексте Start-Job. Так что какой-то способ принудительного разрешения переменных в контексте оболочки также будет приемлемым ответом на это.

Итак, вот что я пробовал, просто для полноты:

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

person jdmichal    schedule 08.02.2010    source источник


Ответы (6)


Возможное решение - создать "кикер-скрипт":

Start-Job -filepath .\emacs.ps1 -ArgumentList $workingdir, "RandomFile.txt"

Ваш сценарий будет выглядеть так:

Set-Location $args[0]
emacs $args[1]

Надеюсь это поможет.

person Filburt    schedule 11.02.2010
comment
Это действительно хорошо сработало. -ArgumentList - это именно то, что я искал. - person jdmichal; 17.02.2010
comment
Имейте в виду, что это будет работать, только если $workingdir не содержит пробелов или других специальных символов. - person DiscoInfiltrator; 05.02.2013
comment
@DiscoInfiltrator: передача только $workingdir отлично работает в PowerShell, даже со встроенными пробелами и другими метасимволами оболочки (вы, вероятно, думаете о оболочках, подобных POSIX); проверить с $dir = 'C:\Program Files\'; function foo() { set-location $args[0] }; foo $dir. - person mklement0; 09.12.2016
comment
@ Filburt, почему вы называете это кикер-скриптом? - person Bowi; 28.03.2018
comment
@Bowi Это действительно просто личное именование - сценарий, запускающий другой сценарий или исполняемый файл. - person Filburt; 28.03.2018

Есть хорошее решение из раздела комментариев какого-то чувака опубликовать. Комментатор предлагает использовать параметр Init для настройки рабочего каталога блока скрипта.

function start-jobhere([scriptblock]$block) {
    Start-Job -Init ([ScriptBlock]::Create("Set-Location '$pwd'")) -Script $block
}
person asavartsov    schedule 30.06.2013
comment
++ для наиболее общего решения: не требуется вспомогательный сценарий, не требуется входной аргумент, который блок фонового сценария должен знать и обрабатывать. - person mklement0; 12.06.2016
comment
это должно иметь кавычки вокруг $ pwd для обработки пробелов в пути - person ghostbust555; 06.07.2016
comment
@ ghostbust555 правильный, одинарные кавычки необходимы, потому что это не ссылка на переменную, а расширение переменной в строке. - person Sander; 29.05.2017
comment
@Sander: Действительно (спасибо за исправление - я пропустил, что создается строка). Однако есть небольшая вероятность, что это сломается, учитывая, что имена файлов могут содержать ' экземпляры, поэтому полностью надежным решением будет: "Set-Location '$($pwd -replace "'", "''")'" - person mklement0; 08.08.2017

Обновление:

PowerShell [Core] 7.0 содержит следующие улучшения:

  • Фоновые задания (а также задания потоков и ForEach-Object -Parallel) теперь - разумно - наследуют текущее местоположение вызывающего (файловая система).

  • Новый параметр -WorkingDirectory позволяет явно указать каталог.


Подводя итог проблеме (применимо к Windows PowerShell и PowerShell Core 6.x)

  • Фоновое задание, запущенное с Start-Job, не наследует текущее местоположение (рабочий каталог).

    • По умолчанию это $HOME\Documents в Windows PowerShell и $HOME в PowerShell Core.

    • Если вы используете PowerShell Core и нацелены на файл в текущем каталоге, вы можете использовать следующее, потому что новый синтаксис ... & по умолчанию запускает задание в текущем каталоге:
      emacs RandomFile.txt &

  • Вы не можете напрямую ссылаться на переменные из текущего сеанса в блоке скрипта, переданном в Start-Job.

Чтобы дополнить собственный полезный ответ jdmichal и полезный ответ Асаварцова, который по-прежнему хорошо работает:

PSv3 представил простой способ ссылки на значения переменных текущего сеанса в блоке сценария, который выполняется в другом контексте (как фоновое задание или на удаленном компьютере), через область $using::

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

Альтернативно:

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

См. about_Remote_Variables


Примечание:

  • 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
person mklement0    schedule 09.12.2016

попробуй это

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

Здесь $input - это предопределенная переменная, которая внутренне принимает значение параметра -inputobject.

person Ritesh    schedule 20.11.2010

Start-Job - это лишнее для того, что вам нужно (выполнение команды в фоновом режиме). Вместо этого используйте Start-Process:

Start-Process -NoNewWindow emacs RandomFile.txt

В этом подходе нет проблем с текущим каталогом. Я также создал функцию, чтобы сделать это как можно проще:

function bg() {Start-Process -NoNewWindow @args}

а затем призыв становится:

bg emacs RandomFile.txt

Это работает в Windows 7 (Powershell v2).

person Bogdan Calmac    schedule 05.12.2012
comment
Но Start-Process сохраняется после закрытия терминала? - person CMCDragonkai; 22.04.2014

Для полноты картины, вот последний сценарий, который я реализовал на основе ответа Филбурта в стиле вики-сообщества:

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