在PowerShell中,如何在文件中定义函数并从PowerShell命令行调用它?


242

我有一个.ps1文件,我想在其中定义自定义函数。

想象一下该文件名为MyFunctions.ps1,内容如下:

Write-Host "Installing functions"
function A1
{
    Write-Host "A1 is running!"
}
Write-Host "Done"

要运行此脚本并从理论上注册A1函数,我导航到.ps1文件所在的文件夹并运行该文件:

.\MyFunctions.ps1

输出:

Installing functions
Done

但是,当我尝试调用A1时,我只是得到了错误,指出该名称没有命令/函数:

The term 'A1' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling
 of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:3
+ A1 <<<<
    + CategoryInfo          : ObjectNotFound: (A1:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

我必须误解一些PowerShell概念。我不能在脚本文件中定义函数吗?

请注意,我已经将执行策略设置为“ RemoteSigned”。而且我知道要在文件名前面使用一个点来运行.ps1文件:。\ myFile.ps1


Answers:


262

在PowerShell命令行上尝试以下操作:

. .\MyFunctions.ps1
A1

点运算符用于脚本包含。


11
好吧,这意味着“在当前上下文而不是子上下文中运行它”。
JasonMArcher

15
这意味着采购文件的内容。和bash一样ss64.com/bash/period.html
inquam 2012年

2
除非您首先运行。\ MyFunctions.ps1使它可用,否则它似乎并不能很好地运行(至少从ISE中起)。我不确定要严格从powershell.exe运行。
Mike Cheel 2013年

1
我认为,点源使用相对于pwd的路径而不是脚本是不符合直觉的,因此我敦促人们改用JoeG的回答并使用模块。
Spork 2014年

5
@Spork . "$PSScriptRoot\MyFunctions.ps1"。Availalbe从v3开始,在此之前请参阅stackoverflow.com/questions/3667238/…。这很常见。
yzorg 2015年

231

您所说的就是点采购。这是邪恶的。但是不用担心,有一种更好,更轻松的方法可以用模块来完成您想要的事情(听起来比现在更可怕)。使用模块的主要好处是,您可以根据需要从外壳程序中卸载它们,并且可以防止函数中的变量爬入外壳程序中(一旦您点了源代码功能文件,请尝试从模块中调用其中一个变量)在外壳中运行,您会明白我的意思)。

因此,首先,将包含所有功能的.ps1文件重命名为MyFunctions.psm1(您刚刚创建了一个模块!)。现在,要使模块正确加载,您必须对文件进行一些特定的操作。首先,Import-Module要查看模块(使用此cmdlet将模块加载到外壳中),它必须位于特定位置。modules文件夹的默认路径是$ home \ Documents \ WindowsPowerShell \ Modules。

在该文件夹中,创建一个名为MyFunctions的文件夹,并将MyFunctions.psm1文件放入其中(模块文件必须位于与PSM1文件名称完全相同的文件夹中)。

完成后,打开PowerShell,然后运行以下命令:

Get-Module -listavailable

如果看到一个名为MyFunctions的文件,则说明操作正确,并且可以加载模块(这只是为了确保正确设置,您只需执行一次)。

要使用该模块,请在外壳程序中键入以下内容(或将此行放入$ profile中,或将此行作为脚本的第一行):

Import-Module MyFunctions

现在可以运行您的功能。有趣的是,一旦您拥有10-15个功能,就将忘记一对夫妇的名字。如果它们在模块中,则可以运行以下命令以获取模块中所有功能的列表:

Get-Command -module MyFunctions

这非常好,而且在正面安装时只需付出一点点努力就值得。


6
如果您的功能仅与该给定的PowerShell应用程序相关,该怎么办?我的意思是,如果您安装PS1软件包以在某处完成工作,则可能不希望配置文件中包含所有功能,对吗?
伊恩·帕特里克·休斯

3
在这种情况下,我将为该特定应用程序创建一个模块,然后在运行脚本之前加载它(如果是交互式工作),或者在脚本内加载它。但是通常来说,如果您具有仅特定于给定任务的代码,则需要脚本中的那些功能。就我个人而言,我只编写通常做一件事的函数。如果一段代码是高度专业化的,那么将其包装在一个函数或模块中实际上没有任何意义(除非有多个脚本使用同一代码,否则这才有意义)。
JoeG 2011年

16
不需要将模块文件放在与PSM1文件完全相同的文件夹中。可以这样完成 Import-Module .\buildsystem\PSUtils.psm1
Michael Freidgeim

2
@MichaelFreidgeim是否简单,如只需更改.with Import-Module并重命名扩展名,并且不需要将模块放置在特定的文件夹中,即我可以将其放置在想要的任何目录中,就像使用点源一样考虑到范围界定带来的好处,甚至没有理由对模块进行点源采购吗?(当然,除非您想要这些范围的“问题”)
Abdul

2
@Abdul,点源更简单,模块功能更强大。见stackoverflow.com/questions/14882332/...
迈克尔Freidgeim

17

. "$PSScriptRoot\MyFunctions.ps1" MyA1Func

Availalbe从v3开始,在此之前请参阅如何获取PowerShell脚本的文件系统位置?。这很常见。

PS:我不赞成“一切都是模块”规则。我的脚本供GIT之外的其他开发人员使用,因此我不希望在脚本运行之前将内容放到特定的位置或修改系统环境变量。这只是一个脚本(或两个或三个)。


FWIW,您无需执行任何这些操作即可在模块中运行脚本。
尼克·考克斯

@NickCox我很乐意看到一些例子。你有什么?+10(如果示例来自OSS项目)。具体来说,是通过相对路径(不是PSModulePath或未自定义PSModulePath)加载的PS模块的示例,以及非平凡的示例(即,与常规脚本作用域相比有好处的模块)。
yzorg

我经常从相对路径导入FluentMigrator.PowerShell模块。这样我们就可以将其检入源代码管理中,并确保每个人都使用相同的版本。它运作良好。
尼克·考克斯

我不确定将其打包为模块还是脚本的相对优缺点:也许那是与作者讨论的内容?我想的灵活Get-Command -Module FluentMigrator.PowerShell度很好吗?
尼克·考克斯

@NickCox您没有完全限定该命令中的模块路径,这意味着除非将模块复制到全局模块文件夹中或将GIT文件夹添加到全局环境变量中,否则找不到该路径。我想你只是证明了我的观点。
yzorg

7

您当然可以在脚本文件中定义函数(然后,我倾向于在加载时通​​过我的Powershell配置文件加载它们)。

首先,您需要检查以确保通过运行以下命令加载了该函数:

ls function:\ | where { $_.Name -eq "A1"  }

并检查它是否出现在列表中(应该是1!),然后让我们知道您将获得什么输出!


1
在PowerShell中,函数被视为目录,因此与说c:\或d:\相同。同样,您也可以在没有反斜杠的情况下使用它,因此ls功能:其中{$ _。Name -eq“ A1”}
Jonny 2013年

4

您可以将功能添加到:

c:\Users\David\Documents\WindowsPowerShell\profile.ps1

该功能将可用。


3

如果您的文件只有一个要调用/公开的主要功能,那么您也可以使用以下命令启动文件:

Param($Param1)

然后可以按如下方式调用它:

.\MyFunctions.ps1 -Param1 'value1'

如果您想轻松地仅调用该函数而不必导入该函数,则将更加方便。


我还应该指出,我今天发现(在我的一位同事告诉我之后)PowerShell自动添加了该[CmdletBinding()]属性并将其免费升级为高级功能。:-)
bergmeister

1

假设您有一个名为Dummy-Name.psm1的模块文件,该文件具有一个称为Function-Dumb()的方法。

Import-Module "Dummy-Name.psm1";
Get-Command -Module "Function-Dumb";
#
#
Function-Dumb;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.