Викличте скрипт PowerShell PS1 з іншого сценарію PS1 всередині IS Powershell


138

Я хочу виконати виклик для сценарію myScript1.ps1 всередині другого сценарію myScript2.ps1 всередині ISE Powershell.

Наступний код у MyScript2.ps1 добре працює від адміністрування Powershell, але не працює в PowerShell ISE:

#Call myScript1 from myScript2
invoke-expression -Command .\myScript1.ps1

Я отримую таку помилку під час виконання MyScript2.ps1 з PowerShell ISE:

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

Відповіді:


83

Для того, щоб знайти місцезнаходження сценарію, використовуйте Split-Path $MyInvocation.MyCommand.Path(переконайтеся, що ви використовуєте це в контексті сценарію).

Причина, коли ви повинні використовувати це, а не що-небудь інше, може бути проілюстрована цим прикладом сценарію.

## ScriptTest.ps1
Write-Host "InvocationName:" $MyInvocation.InvocationName
Write-Host "Path:" $MyInvocation.MyCommand.Path

Ось деякі результати.

PS C: \ Користувачі \ JasonAr>. \ ScriptTest.ps1
InvocationName:. \ ScriptTest.ps1
Шлях: C: \ Користувачі \ JasonAr \ ScriptTest.ps1

PS C: \ Користувачі \ JasonAr>. . \ ScriptTest.ps1
InvocationName:.
Шлях: C: \ Користувачі \ JasonAr \ ScriptTest.ps1

PS C: \ Користувачі \ JasonAr> & ". \ ScriptTest.ps1"
Ім'я виклику: &
Шлях: C: \ Користувачі \ JasonAr \ ScriptTest.ps1

У PowerShell 3.0 та пізніших версіях ви можете використовувати автоматичну змінну $PSScriptRoot:

## ScriptTest.ps1
Write-Host "Script:" $PSCommandPath
Write-Host "Path:" $PSScriptRoot
PS C: \ Користувачі \ jarcher>. \ ScriptTest.ps1
Сценарій: C: \ Користувачі \ jarcher \ ScriptTest.ps1
Шлях: C: \ Користувачі \ jarcher

Пізнє доповнення: Якщо ви переживаєте про відхилення в роботі (або, власне, просто хочете "твердого" коду), ви, ймовірно, хочете використовувати "Write-Output", а не "Write-Host".
KlaymenDK

20
Було б добре побачити у відповіді приклад використання спліт-шлях. Вам також потрібно показати виклик сценарію всередині іншого сценарію.
Джеремі

37

Поточний шлях MyScript1.ps1 не такий, як myScript2.ps1. Ви можете отримати шлях до папки MyScript2.ps1 і з'єднати його з MyScript1.ps1, а потім виконати його. Обидва сценарії повинні бути в одному місці.

## MyScript2.ps1 ##
$ScriptPath = Split-Path $MyInvocation.InvocationName
& "$ScriptPath\MyScript1.ps1"

Як мені потрібно ініціалізувати змінну $ MyInvocation?
Nicola Celiento

Ні, це автоматична змінна.
Шей Леві

Він працює, але отримайте таку помилку перед реальним виконанням виклику скрипту: Split-Path: Неможливо прив’язати аргумент до параметра 'Path', оскільки це порожній рядок. У рядку: 4 напівкоксу: 25 + $ ScriptPath = Split-Path <<<< $ MyInvocation.InvocationName + CategoryInfo: InvalidData: (:) [Split-Path], ParameterBindingValidationException + FullyQualifiedErrorId: ParameterArgumentValidationErrorEmptyStringNotAllowed, Microsoft.PowerShell.Commands.SplitPathCommand
Nicola Celiento

створити новий сценарій put: $ MyInvocation.InvocationName у ньому та запустіть сценарій. Чи отримуєте ви шлях сценарію?
Шей Леві

@JasonMArcher - чому замість цього? Наскільки я знаю, обидва дають однаковий вихід?
manojlds

36

Я дзвоню myScript1.ps1 з myScript2.ps1.

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

$PSScriptRoot

Потім додайте ім'я сценарію, яке ви хочете викликати так:

& "$PSScriptRoot\myScript1.ps1"

Це має спрацювати.


3
& "$PSScriptRoot\myScript1.ps1"достатньо
Вейхуй Го

19

Рішення з однієї лінії:

& ((Split-Path $MyInvocation.InvocationName) + "\MyScript1.ps1")

Це добре, але чому б не просто, & '.\MyScript1.ps'якщо сценарій знаходиться в одному каталозі?
JoePC

3
Для цього використовується поточний каталог, а не каталог сценаріїв. Звичайно, вони часто однакові… але не завжди!
noelicus

9

Це лише додаткова інформація для відповідей, щоб передати аргумент в інший файл

Де ви очікуєте аргументу

PrintName.ps1

Param(
    [Parameter( Mandatory = $true)]
    $printName = "Joe"    
)


Write-Host $printName

Як викликати файл

Param(
    [Parameter( Mandatory = $false)]
    $name = "Joe"    
)


& ((Split-Path $MyInvocation.InvocationName) + "\PrintName.ps1") -printName $name

Якщо ви не надасте жодного вводу, він за замовчуванням буде "Joe", і це буде передано як аргумент у аргумент printName у файлі PrintName.ps1, який, у свою чергу, роздрукує рядок "Joe"


4

Можливо, ви вже знайшли відповідь на це, але ось що я роблю.

Я зазвичай розміщую цей рядок на початку моїх сценаріїв установки:

if(!$PSScriptRoot){ $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent } #In case if $PSScriptRoot is empty (version of powershell V.2).  

Тоді я можу використовувати змінну $ PSScriptRoot як місце розташування поточного сценарію (шляху), як у наведеному нижче прикладі:

if(!$PSScriptRoot){ $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent } #In case if $PSScriptRoot is empty (version of powershell V.2).  

Try {
If (Test-Path 'C:\Program Files (x86)') {
    $ChromeInstallArgs= "/i", "$PSScriptRoot\googlechromestandaloneenterprise64_v.57.0.2987.110.msi", "/q", "/norestart", "/L*v `"C:\Windows\Logs\Google_Chrome_57.0.2987.110_Install_x64.log`""
    Start-Process -FilePath msiexec -ArgumentList $ChromeInstallArgs -Wait -ErrorAction Stop
    $Result= [System.Environment]::ExitCode
} Else {
    $ChromeInstallArgs= "/i", "$PSScriptRoot\googlechromestandaloneenterprise_v.57.0.2987.110.msi", "/q", "/norestart", "/L*v `"C:\Windows\Logs\Google_Chrome_57.0.2987.110_Install_x86.log`""
    Start-Process -FilePath msiexec -ArgumentList $ChromeInstallArgs -Wait -ErrorAction Stop
    $Result= [System.Environment]::ExitCode
    }

} ### End Try block


Catch  {
    $Result = [System.Environment]::Exitcode
    [System.Environment]::Exit($Result)
   }
[System.Environment]::Exit($Result)

У вашому випадку ви можете замінити

Почати-обробити ... рядок із

Виклик-вираз $ PSScriptRoot \ ScriptName.ps1

Докладніше про автоматичні змінні $ MYINVOCATION та $ PSScriptRoot можна прочитати на веб-сайті Microsoft: https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_automatic_variables


4

Щоб легко виконати файл сценарію в тій же папці (або підпапці), що і абонент, ви можете скористатися цим:

# Get full path to the script:
$ScriptRoute = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($PSScriptRoot, "Scriptname.ps1"))

# Execute script at location:
&"$ScriptRoute"

3

У мене була проблема з цим. Я не використовував жодних розумних $MyInvocationречей, щоб виправити це. Якщо ви відкриєте ISE, натиснувши правою кнопкою миші файл сценарію та вибравши, editа потім відкрити другий скрипт у межах ISE, ви можете викликати один з іншого просто за допомогою звичайного синтаксису. \ Script.ps1 . Я здогадуюсь, що ISE має поняття поточної папки, і відкриття так, як це встановлює поточну папку в папку, що містить сценарії. Коли я звичайно використовую один сценарій від іншого, я просто використовую . \ Script.ps1 , IMO неправильно змінювати скрипт лише для того, щоб він працював в ISE належним чином ...


2

У мене була подібна проблема і вирішилася її таким чином.

Мій робочий каталог - це загальна папка скриптів і конкретна папка скриптів у тому ж корені, мені потрібно викликати певну папку скриптів (які викликають загальний скрипт із параметром конкретної проблеми). Тож робочий каталог такий

\Nico\Scripts\Script1.ps1
             \Script2.ps1
      \Problem1\Solution1.ps1
               \ParameterForSolution1.config
      \Problem2\Solution2.ps1
               \ParameterForSolution2.config

Solutions1 і Solutions2 викликають PS1 у папці Scripts, завантажуючи параметр, збережений у ParameterForSolution. Отже, в ISSL-силі запускаю цю команду

.\Nico\Problem1\Solution1.PS1

А код всередині Solution1.PS1:

# This is the path where my script is running
$path = split-path -parent $MyInvocation.MyCommand.Definition

# Change to root dir
cd "$path\..\.."

$script = ".\Script\Script1.PS1"

$parametro = "Problem1\ParameterForSolution1.config"
# Another set of parameter Script1.PS1 can receive for debuggin porpuose
$parametro +=' -verbose'

Invoke-Expression "$script $parametro"

2

Я подаю свій приклад на розгляд. Ось так я називаю деякий код із сценарію контролера в інструментах, які я створюю. Сценарії, які виконують роботу, також повинні приймати параметри, тому цей приклад показує, як їх передавати. Він передбачає, що викликаний скрипт знаходиться в тому самому каталозі, що і сценарій контролера (сценарій, що робить виклик).

[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string[]]
$Computername,

[Parameter(Mandatory = $true)]
[DateTime]
$StartTime,

[Parameter(Mandatory = $true)]
[DateTime]
$EndTime
)

$ZAEventLogDataSplat = @{
    "Computername" = $Computername
    "StartTime"    = $StartTime
    "EndTime"      = $EndTime
}

& "$PSScriptRoot\Get-ZAEventLogData.ps1" @ZAEventLogDataSplat

Наведене вище - сценарій контролера, який приймає 3 параметри. Вони визначені в блоці парам. Потім сценарій контролера викликає скрипт з назвою Get-ZAEventLogData.ps1. Для прикладу, цей сценарій також приймає ті самі 3 параметри. Коли скрипт контролера викликає сценарій, який виконує роботу, йому потрібно викликати його і передати параметри. Наведене вище показує, як я це роблю, бризкаючи.


1

Як ви запускаєте вбудовані сценарії PowerShell всередині своїх сценаріїв?

Як ви використовуєте вбудовані сценарії на кшталт

Get-Location
pwd
ls
dir
split-path
::etc...

Вони управляються вашим комп'ютером, автоматично перевіряючи шлях сценарію.

Так само я можу запускати свої власні сценарії, просто вводячи ім'я сценарію в скрипт-блок

::sid.ps1 is a PS script I made to find the SID of any user
::it takes one argument, that argument would be the username
echo $(sid.ps1 jowers)


(returns something like)> S-X-X-XXXXXXXX-XXXXXXXXXX-XXX-XXXX


$(sid.ps1 jowers).Replace("S","X")

(returns same as above but with X instead of S)

Перейдіть до командного рядка powershell та введіть

> $profile

Це поверне шлях до файлу, який наш командний рядок PowerShell виконуватиме щоразу, коли ви відкриваєте додаток.

Це буде виглядати приблизно так

C:\Users\jowers\OneDrive\Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1

Перейдіть до "Документи" і перевірте, чи вже у вас є каталог WindowsPowerShell. Я цього не зробив

> cd \Users\jowers\Documents
> mkdir WindowsPowerShell
> cd WindowsPowerShell
> type file > Microsoft.PowerShellISE_profile.ps1

Зараз ми створили сценарій, який запускатиметься кожного разу, коли ми відкриваємо додаток PowerShell.

Причиною цього ми стали те, що ми могли додати власну папку, яка містить усі наші власні сценарії. Створимо цю папку, і я назву її "Bin" після каталогів, у яких Mac / Linux містить свої сценарії.

> mkdir \Users\jowers\Bin

Тепер ми хочемо, щоб цей каталог був доданий до нашої $env:pathзмінної щоразу, коли ми відкриваємо додаток, тому повертаємося до WindowsPowerShellКаталогу та

> start Microsoft.PowerShellISE_profile.ps1

Потім додайте це

$env:path += ";\Users\jowers\Bin"

Тепер оболонка автоматично знайде ваші команди, доки ви збережете свої сценарії в тій папці "Бін".

Відновіть панель повноважень, і це має бути одним із перших сценаріїв, які виконують.

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

> $env:Path

Тепер ми можемо викликати наші сценарії з командного рядка або з іншого сценарію так просто:

$(customScript.ps1 arg1 arg2 ...)

Як бачите, ми повинні зателефонувати їм із .ps1розширенням, поки ми не зробимо для них псевдоніми. Якщо ми хочемо пофантазувати.


Нічого, спасибі за це, тут є багато. Але є вже 9 інших відповідей тут. Чим це відрізняється? Яку додаткову інформацію вона надає?
Стівен Рауч

Це дозволяє нам використовувати наші власні сценарії всередині інших скриптів - таким же чином, як і вбудовані сценарії використовуються всередині наших сценаріїв. Зробіть це, і поки ви збережете свої сценарії в каталозі, який ви вводите на свій шлях, комп'ютер автоматично знайде шлях користувацького сценарію, коли ви його використовуєте в командному рядку або в іншому сценарії
Тайлер Кертіс Джоуерс,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.