Вызов сценария PowerShell PS1 из другого сценария PS1 в Powershell ISE

138

Я хочу выполнить вызов для сценария myScript1.ps1 внутри второго сценария myScript2.ps1 внутри Powershell ISE.

Следующий код внутри 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: \ Users \ JasonAr>. \ ScriptTest.ps1
InvocationName:. \ ScriptTest.ps1
Путь: C: \ Users \ JasonAr \ ScriptTest.ps1

PS C: \ Users \ JasonAr>. . \ ScriptTest.ps1
InvocationName:.
Путь: C: \ Users \ JasonAr \ ScriptTest.ps1

PS C: \ Users \ JasonAr> & ". \ ScriptTest.ps1"
InvocationName: &
Путь: C: \ Users \ JasonAr \ ScriptTest.ps1

В PowerShell 3.0 и более поздних версиях вы можете использовать автоматическую переменную $PSScriptRoot:

## ScriptTest.ps1
Write-Host "Script:" $PSCommandPath
Write-Host "Path:" $PSScriptRoot
PS C: \ Users \ jarcher>. \ ScriptTest.ps1
Скрипт: C: \ Users \ jarcher \ ScriptTest.ps1
Путь: C: \ Users \ jarcher
JasonMArcher
источник
Позднее добавление: если вас беспокоит дисперсия (или, на самом деле, просто нужен «твердый» код), вы, вероятно, захотите использовать «Write-Output», а не «Write-Host».
KlaymenDK
20
Было бы хорошо увидеть пример использования Split-Path в ответе. Вы также должны показать вызов скрипта внутри другого скрипта.
Джереми
37

Текущий путь к MyScript1.ps1 отличается от myScript2.ps1. Вы можете получить путь к папке MyScript2.ps1 и объединить его с MyScript1.ps1, а затем выполнить его. Оба сценария должны находиться в одном месте.

## MyScript2.ps1 ##
$ScriptPath = Split-Path $MyInvocation.InvocationName
& "$ScriptPath\MyScript1.ps1"
Шей Леви
источник
Как мне инициализировать переменную $ MyInvocation?
Никола Селиенто
Вы не делаете, это автоматическая переменная.
Шей Леви
Он работает, но перед фактическим выполнением вызываемого скрипта возникает следующая ошибка: Split-Path: невозможно привязать аргумент к параметру «Путь», потому что это пустая строка. В строке: 4 символа: 25 + $ ScriptPath = Split-Path <<<< $ MyInvocation.InvocationName + CategoryInfo: InvalidData: (:) [Split-Path], ParameterBindingValidationException + FullyQualifiedErrorId: ParameterArgumentValidationErrorEmplithString
Никола Celiento
создайте новый скрипт, поместите в него: $ MyInvocation.InvocationName и запустите скрипт. Вы понимаете путь сценария?
Шей Леви
@JasonMArcher - Почему вместо этого? Насколько я знаю, оба дают одинаковый вывод?
manojlds
36

Я вызываю myScript1.ps1 из myScript2.ps1.

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

$PSScriptRoot

А затем добавьте имя скрипта, который вы хотите вызвать, следующим образом:

& "$PSScriptRoot\myScript1.ps1"

Это должно работать.

Сриджани Гош
источник
3
& "$PSScriptRoot\myScript1.ps1"достаточно
Вэйхуэй Го
19

Однострочное решение:

& ((Split-Path $MyInvocation.InvocationName) + "\MyScript1.ps1")
noelicus
источник
Это хорошо, но почему бы и нет, & '.\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»

cpoDesign
источник
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)

В вашем случае вы можете заменить

Start-process ... в соответствии с

Вызвать-выражение $ 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"
Rrr
источник
3

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

Sabroni
источник
2

У меня была похожая проблема, и я решил ее таким образом.

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

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

Solutions1 и Solutions2 вызывают PS1 в папке Scripts, загружая параметр, сохраненный в ParameterForSolution. Так что в powershell ISE я запускаю эту команду

.\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"

Теперь оболочка автоматически найдет ваши команды, пока вы сохраняете скрипты в этом каталоге «Bin».

Перезапустите powershell, и он должен быть одним из первых выполняемых скриптов.

Запустите это в командной строке после перезагрузки, чтобы увидеть новый каталог в переменной пути:

> $env:Path

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

$(customScript.ps1 arg1 arg2 ...)

Как видите, мы должны вызывать их с .ps1расширением, пока не создадим для них псевдонимы. Если мы хотим стать модным.

Тайлер Кертис Джоуэрс
источник
Вау, спасибо за это, здесь много всего. Но здесь уже есть 9 других ответов. Чем это отличается? Какую дополнительную информацию она предоставляет?
Стивен Раух
Это позволяет нам использовать наши пользовательские сценарии внутри других сценариев - так же, как встроенные сценарии используются внутри наших сценариев. Сделайте это, и пока вы сохраняете свои сценарии в каталоге, который вы указали в своем пути, компьютер автоматически найдет путь пользовательского сценария, когда вы используете его в командной строке или в другом сценарии
Тайлер Кертис Джоуэрс