PowerShell: выполнить команду из каталога сценария

147

У меня есть сценарий PowerShell, который кое-что делает, используя текущий каталог сценария. Итак, внутри этого каталога все .\script.ps1работает правильно.

Теперь я хочу вызвать этот сценарий из другого каталога, не меняя каталог, на который ссылается сценарий. Итак, я хочу вызвать ..\..\dir\script.ps1и по-прежнему хочу, чтобы этот сценарий вел себя так, как он был вызван из своего каталога.

Как мне это сделать или как изменить сценарий, чтобы он мог запускаться из любого каталога?

тыкать
источник

Ответы:

202

Вы имеете в виду, что вам нужен собственный путь к сценарию, чтобы вы могли ссылаться на файл рядом со сценарием? Попробуй это:

$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath
Write-host "My directory is $dir"

Вы можете получить много информации из $ MyInvocation и его свойств.

Если вы хотите сослаться на файл в текущем рабочем каталоге, вы можете использовать Resolve-Path или Get-ChildItem:

$filepath = Resolve-Path "somefile.txt"

ИЗМЕНИТЬ (на основе комментария OP):

# temporarily change to the correct folder
Push-Location $folder

# do stuff, call ant, etc

# now back to previous directory
Pop-Location

Вероятно, есть и другие способы добиться чего-то подобного с помощью Invoke-Command.

JohnL
источник
1
Хм, ладно, может мне стоило быть более ясным в отношении моего сценария. Фактически это скрипт, который вызывается antс некоторыми параметрами. Поэтому мне нужно позвонить antиз этой папки, чтобы убедиться, что он правильно нашел файл конфигурации. В идеале я ищу что-нибудь для временного изменения каталога выполнения локально внутри этого сценария.
poke
3
А то в таком случае вы будете хотеть Push-LocationиPop-Location
JohnL
5
будьте осторожны, $ MyInvocation зависит от контекста, я обычно использую $ ScriptPath = (Get-Variable MyInvocation -Scope Script) .Value.MyCommand.Path, который работает с любым
41

Есть ответы с большим количеством голосов, но когда я прочитал ваш вопрос, я подумал, что вы хотите знать каталог, в котором находится скрипт, а не тот, где скрипт запущен. Вы можете получить информацию с помощью автоматических переменных PowerShell.

$PSScriptRoot - the directory where the script exists, not the target directory the script is running in
$PSCommandPath - the full path of the script

Например, у меня есть скрипт $ profile, который находит файл решения Visual Studio и запускает его. Я хотел сохранить полный путь после запуска файла решения. Но я хотел сохранить файл, в котором существует исходный сценарий. Итак, я использовал $ PsScriptRoot.

Энди
источник
2
Ответ, который я искал. Другие, кажется, решают больше проблем, чем одна.
Арвинд Мани,
41

Если вы вызываете собственные приложения, вам нужно беспокоиться [Environment]::CurrentDirectoryне о $PWDтекущем каталоге PowerShell . По разным причинам PowerShell не устанавливает текущий рабочий каталог процесса при Set-Location или Push-Location, поэтому вам необходимо убедиться, что вы это сделали, если вы запускаете приложения (или командлеты), которые ожидают его установки.

В сценарии вы можете сделать это:

$CWD = [Environment]::CurrentDirectory

Push-Location $MyInvocation.MyCommand.Path
[Environment]::CurrentDirectory = $PWD
##  Your script code calling a native executable
Pop-Location

# Consider whether you really want to set it back:
# What if another runspace has set it in-between calls?
[Environment]::CurrentDirectory = $CWD

Этому нет надежной альтернативы. Многие из нас помещают строку в нашу функцию приглашения, чтобы установить [Environment] :: CurrentDirectory ... но это не помогает вам, когда вы меняете местоположение в сценарии.

Два примечания о причине, по которой это не устанавливается PowerShell автоматически:

  1. PowerShell может быть многопоточным. У вас может быть несколько Runspaces (см. RunspacePool и модуль PSThreadJob), работающих одновременно в одном процессе. Каждое пространство выполнения имеет свой собственный $PWDрабочий каталог, но есть только один процесс и только одна среда.
  2. Даже если вы однопоточны, $PWDэто не всегда законный CurrentDirectory (например, вы можете записать CD в провайдер реестра).

Если вы хотите поместить его в свое приглашение (которое будет работать только в основном пространстве выполнения, однопоточное), вам необходимо использовать:

[Environment]::CurrentDirectory = Get-Location -PSProvider FileSystem
Джайкул
источник
4
Он не меняет рабочий каталог процесса из-за поставщиков пути: вы можете записывать компакт-диски в реестр, базу данных SQL или куст IIS и т. Д.
piers7
1
Да. Я знаю, в этом был смысл последней "Заключительной ноты". Они могли бы (как я это делаю в своей функции приглашения) обновить его до текущего местоположения поставщика FileSystem, как и любую другую оболочку в мире.
Jaykul 08
2
Святые боги сценариев, я ТАК РАЗОЧАРОВАЛСЯ .\_/.- это убило половину моего дня! Народ, серьезно? Серьезно? ..
ulidtko
2
Сейчас в этом практически нет необходимости, более современные версии PowerShell устанавливают рабочий каталог при запуске двоичных приложений.
Jaykul
1
Этот метод не может восстановить оригинал, [Environment]::CurrentDirectoryесли он отличается от $PWD. Правильнее было бы сохранить его в переменной, $origEnvDir = [Environment]::CurrentDirectoryа затем восстановить [Environment]::CurrentDirectory = $origEnvDir.
Strom
12

Я часто использовал следующий код для импорта модуля, который находится в том же каталоге, что и запущенный скрипт. Сначала он получит каталог, из которого запущен powershell

$ currentPath = Split-Path ((Get-Variable MyInvocation -Scope 0) .Value) .MyCommand.Path

import-module "$ currentPath \ sqlps.ps1"

Даниэль Ву
источник
вы хотите установить -Scope в Script
10

Это сработает нормально.

Push-Location $PSScriptRoot

Write-Host CurrentDirectory $CurDir
АрунСК
источник
Не забудьте вызвать Pop-Locationв конце, чтобы вернуть путь в исходное состояние.
Лам Ле
2

Что ж, я какое-то время искал решение для этого, без каких-либо скриптов только из CLI. Вот как я это делаю xD:

  • Перейдите в папку, из которой вы хотите запустить скрипт (важно, чтобы у вас были дополнения вкладок)

    ..\..\dir

  • Теперь заключите location в двойные кавычки и добавьте внутри них cd, чтобы мы могли вызвать другой экземпляр powershell.

    "cd ..\..\dir"

  • Добавьте еще одну команду для запуска скрипта, разделенную ; , с разделителем команд в PowerShell

    "cd ..\..\dir\; script.ps1"

  • Наконец, запустите его с другим экземпляром powershell

    start powershell "cd..\..\dir\; script.ps1"

Это откроет новое окно PowerShell, перейдите в ..\..\dir , запустите script.ps1и закройте окно.


Обратите внимание, что ";" просто разделяет команды, как если бы вы вводили их одну за другой, если первая не удалась, вторая будет запущена и следующая после, а затем после ... Если вы хотите, чтобы новое окно PowerShell было открыто, вы добавляете -noexit в переданную команду. Обратите внимание, что сначала я перехожу к желаемой папке, чтобы использовать завершение табуляции (вы не могли заключать их в двойные кавычки).

start powershell "-noexit cd..\..\dir\; script.ps1"

Используйте двойные кавычки, ""чтобы вы могли передавать каталоги с пробелами в именах, например,

start powershell "-noexit cd '..\..\my dir'; script.ps1"

ИГРАЧ
источник
0

Я сделал однострочник из решения @ JohnL:

$MyInvocation.MyCommand.Path | Split-Path | Push-Location
сашоальм
источник