PowerShell: запустити команду з каталогу скриптів


144

У мене є сценарій PowerShell, який робить деякі речі, використовуючи поточний каталог сценарію. Тож, коли всередині цього каталогу, запуск .\script.ps1працює правильно.

Тепер я хочу зателефонувати цьому скрипту з іншого каталогу, не змінюючи довідковий каталог сценарію. Тому я хочу зателефонувати ..\..\dir\script.ps1і все ще хочу, щоб цей сценарій поводився так, як його викликали всередині його каталогу.

Як це зробити чи як змінити сценарій, щоб він міг працювати з будь-якого каталогу?

Відповіді:


200

Ви маєте на увазі, що хочете власний шлях сценарію, щоб ви могли посилатися на файл поруч із сценарієм? Спробуйте це:

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

Ви можете отримати багато інформації від $ MyInvocation та його властивостей.

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

$filepath = Resolve-Path "somefile.txt"

EDIT (на основі коментаря від ОП):

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

# do stuff, call ant, etc

# now back to previous directory
Pop-Location

Можливо, є й інші способи досягнення чогось подібного, використовуючи Invoke-Command.


1
Хм, гаразд, можливо, я мав би бути трохи більш зрозумілим щодо свого сценарію. Насправді це сценарій, який викликає antдеякі параметри. Тому мені потрібно зателефонувати antз цієї папки, щоб переконатися, що він правильно знайшов файл конфігурації. В ідеалі я шукаю щось, щоб тимчасово змінити каталог виконання локально в рамках цього сценарію.
ткнути

3
А тоді в такому випадку вам захочеться Push-LocationіPop-Location
ДжонЛ

5
уважно з цього приводу, $ MyInvocation є контекстно-залежним, я зазвичай роблю $ ScriptPath = (Get-Variable MyInvocation -Scope Script) .Value.MyCommand.Path, який працює з будь-якого типу гніздування або функції

39

Якщо ви телефонуєте на рідні програми, вам потрібно потурбуватися про те, щоб [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 (ви можете, наприклад, компакт-диск в постачальника реєстру).

Якщо ви хочете вставити його у своє запит (який би запускався лише в основному просторі, однопотоковий), вам потрібно використовувати:

[Environment]::CurrentDirectory = Get-Location -PSProvider FileSystem

4
Це не змінює робочий каталог процесів через постачальників маршрутів: ви можете записати CD в реєстр, базу даних SQL або вулик IIS тощо ...
piers7

1
Так. Я знаю, це був пункт останньої "Заключної ноти". Вони могли (як і в моїй функції підказок) оновити його до поточного місцезнаходження постачальника FileSystem, як і будь-яка інша оболонка у світі.
Джейкуль

2
Святі боги сценаріїв, я настільки розчарований .\_/.- за це вбили половину мого дня! Люди, серйозно? Серйозно? ..
улідко

2
Зараз це в основному непотрібно, більш сучасні версії PowerShell встановлюють робочий каталог під час запуску бінарних додатків.
Джейкуль

1
Цей метод не в змозі відновити оригінал, [Environment]::CurrentDirectoryколи це відрізняється від $PWD. Правильною справою було б зберегти її в змінну, $origEnvDir = [Environment]::CurrentDirectoryа потім пізніше відновити її [Environment]::CurrentDirectory = $origEnvDir.
Стром

37

Є відповіді з великою кількістю голосів, але коли я читав ваше запитання, я думав, що ви хочете дізнатися каталог, де знаходиться сценарій, а не той, де працює сценарій. Інформацію можна отримати за допомогою автоматичних змінних powershell

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

Наприклад, у мене є $ профіль скрипт, який знаходить файл візуального рішення студії та запускає його. Після запуску файлу рішення я хотів зберегти повний шлях. Але я хотів зберегти файл там, де існує оригінальний сценарій. Тому я використав $ PsScriptRoot.


12

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

$ currentPath = Розділити шлях ((Get-Variable MyInvocation -Scope 0) .Value) .MyCommand.Path

import-модуль "$ currentPath \ sqlps.ps1"


ви хочете встановити -Scope на Script

9

Це буде добре.

Push-Location $PSScriptRoot

Write-Host CurrentDirectory $CurDir

Не забудьте подзвонити Pop-Locationнаприкінці, щоб повернути шлях до початкового стану.
Лам Ле,

2

Ну, я шукав рішення для цього деякий час, без будь-яких сценаріїв лише від CLI. Ось як я це роблю xD:

  • Перейдіть до папки, з якої потрібно запустити скрипт (важливо, що у вас є доповнення вкладки)

    ..\..\dir

  • Тепер оберіть місце розташування з подвійними лапками, а всередині додайте cd, щоб ми могли викликати ще один екземпляр powerhell.

    "cd ..\..\dir"

  • Додайте ще одну команду для запуску сценарію, розділеного на ;, з - це розділювач команд у shellhell

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

  • Нарешті, запустіть його з іншим екземпляром powerhell

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

Це відкриє нове вікно оболонки, перейде до ..\..\dir, запустіть script.ps1і закриє вікно.


Зауважте, що ";" просто розділяє команди, як і ви їх набрали одну за одною, якщо перша невдача, друга запуститься, а наступна після, а наступна після ... Якщо ви хочете зберегти нове вікно, що відкриється, все це відкриється, ви додасте -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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.