ChatGPT解决这个技术问题 Extra ChatGPT

从 Powershell ISE 中的另一个 PS1 脚本调用 PowerShell 脚本 PS1

我想在 Powershell ISE 中的第二个 myScript2.ps1 脚本中调用 myScript1.ps1 脚本。

MyScript2.ps1 中的以下代码在 Powershell 管理中运行良好,但在 PowerShell ISE 中无法运行:

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

从 PowerShell ISE 执行 MyScript2.ps1 时出现以下错误:

术语“.\myScript1.ps1”未被识别为 cmdlet、函数、脚本文件或可运行程序的名称。检查名称的拼写,或者如果包含路径,请验证路径是否正确并重试。


J
JasonMArcher

要查找脚本的位置,请使用 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
Path: C:\Users\JasonAr\ScriptTest.ps1

PS C:\Users\JasonAr> . .\ScriptTest.ps1
InvocationName: .
Path: C:\Users\JasonAr\ScriptTest.ps1

PS C:\Users\JasonAr> & ".\ScriptTest.ps1"
InvocationName: &
Path: 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
Script: C:\Users\jarcher\ScriptTest.ps1
Path: C:\Users\jarcher

一个较晚的补充:如果您担心方差(或者,实际上,只是想要“可靠”代码),您可能想要使用“Write-Output”而不是“Write-Host”。
最好在答案中看到使用拆分路径的示例。您还需要真正显示在另一个脚本中调用脚本。
y
ycomp

我从 myScript2.ps1 调用 myScript1.ps1 。

假设两个脚本位于同一位置,首先使用以下命令获取脚本的位置:

$PSScriptRoot

然后,附加您要调用的脚本名称,如下所示:

& "$PSScriptRoot\myScript1.ps1"

这应该有效。


& "$PSScriptRoot\myScript1.ps1" 就足够了
& $PSScriptRoot\myScript1.ps1 就够了(不带引号)
@GregorSimončič仅当变量不包含空格字符时。如果变量包含空格,则未引用的命令可能会由于将命令“拆分”为两部分而引入语法错误。保留报价是最安全的方法。
S
Shay Levy

MyScript1.ps1 的当前路径与 myScript2.ps1 不同。您可以获取 MyScript2.ps1 的文件夹路径并将其连接到 MyScript1.ps1 然后执行它。两个脚本必须位于同一位置。

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

我必须如何初始化 $MyInvocation 变量?
你不知道,它是一个自动变量。
它可以工作,但在真正执行被调用脚本之前会出现以下错误:Split-Path:无法将参数绑定到参数“Path”,因为它是一个空字符串。在 line:4 char:25 + $ScriptPath = Split-Path <<<< $MyInvocation.InvocationName + CategoryInfo : InvalidData: (:) [Split-Path], ParameterBindingValidationException + FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Microsoft.PowerShell.Commands.SplitPathCommand
在其中创建一个新脚本: $MyInvocation.InvocationName 并运行该脚本。你得到脚本的路径吗?
@JasonMArcher - 为什么?据我所知,两者都给出相同的输出?
n
noelicus

一线解决方案:

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

这很好,但如果脚本位于同一目录中,为什么不直接使用 & '.\MyScript1.ps' 呢?
那是使用当前目录而不是脚本目录。当然,它们通常是相同的……但并非总是如此!
我质疑同一行 - 如果您在调用它之前运行“Set-Location”命令移动到该文件夹,则应该通过设置位置并同时使其成为当前位置来实现。
首先,这不再是单行了:)。但是,它仅适用于您不介意更改工作目录的情况,这是一种假设,而不是好的做法。
c
cpoDesign

这只是答案的附加信息,以便将参数传递到另一个文件

你期望争论的地方

打印名称.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.ps1 文件中的 printName 参数,然后打印出“Joe”字符串


R
Rrr

要在与调用者相同的文件夹(或子文件夹)中轻松执行脚本文件,您可以使用以下命令:

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

# Execute script at location:
&"$ScriptRoute"

E
Emil Bogopolskiy

您可能已经找到了答案,但这就是我所做的。

我通常将此行放在安装脚本的开头:

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

您可以在 Microsoft 网站上阅读有关 $MYINVOCATION 和 $PSScriptRoot 自动变量的更多信息:https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_automatic_variables


N
Nico Osorio

我有一个类似的问题并以这种方式解决了。

我的工作目录是同一个根目录中的一个通用脚本文件夹和几个特定脚本文件夹,我需要调用特定脚本文件夹(它使用特定问题的参数调用通用脚本)。所以工作目录是这样的

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

Solutions1 和 Solutions2 调用 Scripts 文件夹中的 PS1 加载存储在 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"

Z
Zack A

我提交我的例子供考虑。这就是我在我制作的工具中从控制器脚本中调用一些代码的方式。完成这项工作的脚本也需要接受参数,所以这个例子展示了如何传递它们。它确实假设被调用的脚本与控制器脚本(进行调用的脚本)位于同一目录中。

[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 个参数的控制器脚本。这些在 param 块中定义。然后控制器脚本调用名为 Get-ZAEventLogData.ps1 的脚本。例如,此脚本也接受相同的 3 个参数。当控制器脚本调用执行工作的脚本时,它需要调用它并传递参数。上面显示了我是如何通过喷溅来做到这一点的。


P
Pop

我有一个问题。我没有使用任何聪明的 $MyInvocation 东西来修复它。如果您通过右键单击脚本文件并选择 edit 打开 ISE,然后从 ISE 中打开第二个脚本,您可以使用普通 .\script.ps1 语法从另一个脚本调用一个.我的猜测是 ISE 具有当前文件夹的概念,并且像这样打开它会将当前文件夹设置为包含脚本的文件夹。当我在正常使用中从另一个脚本调用一个脚本时,我只使用 .\script.ps1,IMO 修改脚本只是为了使其在 ISE 中正常工作是错误的......


对此投了反对票,因为问题是脚本在不从包含它的文件夹运行时无法工作。这不是要专门修改脚本以在 ISE 中工作,而是要修改脚本以使其具有足够的可移植性和灵活性,以处理从不同目录运行的情况。在这种情况下,代码的行为不应取决于您从何处调用它。
T
Tyler Curtis Jowers

如何在脚本中运行 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

转到 Documents 并查看您是否已有 WindowsPowerShell 目录。我没有,所以

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

我们现在已经创建了每次打开 PowerShell 应用程序时都会启动的脚本。

我们这样做的原因是我们可以添加自己的文件夹来保存我们所有的自定义脚本。让我们创建该文件夹,我将在 Mac/Linux 保存其脚本的目录之后将其命名为“Bin”。

> mkdir \Users\jowers\Bin

现在我们希望每次打开应用程序时将该目录添加到我们的 $env:path 变量中,因此请返回 WindowsPowerShell 目录并

> start Microsoft.PowerShellISE_profile.ps1

然后添加这个

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

现在,只要您将脚本保存在那个“Bin”目录中,shell 就会自动找到您的命令。

重新启动 powershell,它应该是最先执行的脚本之一。

重新加载后在命令行上运行此命令以查看路径变量中的新目录:

> $env:Path

现在我们可以从命令行或从另一个脚本中调用我们的脚本,就像这样:

$(customScript.ps1 arg1 arg2 ...)

如您所见,在我们为它们创建别名之前,我们必须使用 .ps1 扩展名来调用它们。如果我们想变得花哨。


哇,谢谢你,这里有很多。但是这里已经有 9 个其他答案。这有什么不同?它提供了哪些额外信息?
这样做允许我们在其他脚本中使用我们的自定义脚本——就像在我们的脚本中使用内置脚本一样。这样做,只要将脚本保存在路径中的目录中,当您在命令行或其他脚本中使用自定义脚本时,计算机将自动找到该路径
V
Vega

您还可以使用以下命令:

$Patch = Join-Path -Path $PSScriptRoot -ChildPath "\patch.ps1"<br>
Invoke-Expression "& `"$Patch`""

R
Richard H

如果上面的变量为空,您可能正在使用 PowerShell ISE。在这种情况下尝试:

if ($psISE) { Split-Path -Path $psISE.CurrentFile.FullPath } else { $global:PSScriptRoot }

看看这个discussion