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

Posted

技术标签:

【中文标题】在 PowerShell 中,如何在文件中定义函数并从 PowerShell 命令行调用它?【英文标题】:In PowerShell, how do I define a function in a file and call it from the PowerShell commandline? 【发布时间】:2011-08-26 08:43:51 【问题描述】:

我有一个 .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

【问题讨论】:

PS 启动时加载函数的好链接:sandfeld.net/powershell-load-your-functions-at-startup 【参考方案1】:

在 PowerShell 命令行上试试这个:

. .\MyFunctions.ps1
A1

点运算符用于脚本包括,又名“点源”(或"dot source notation")

【讨论】:

嗯,它的意思是“在当前上下文而不是子上下文中运行它。” 表示source这个文件的内容。与 bash 中的相同。 ss64.com/bash/period.html 除非您首先运行 .\MyFunctions.ps1 以使其可用,否则它似乎并不能很好地工作(至少来自 ISE)。我不确定是否严格从 powershell.exe 运行。 我认为点源使用相对于密码而不是脚本的路径是违反直觉的,所以我会敦促人们改用 JoeG 的答案并使用模块。 @Spork . "$PSScriptRoot\MyFunctions.ps1"。从 v3 开始可用,在此之前请参阅 ***.com/questions/3667238/…。这很常见。【参考方案2】:

你所说的叫dot sourcing。这是邪恶的。不过不用担心,使用 modules 有一种更好、更简单的方法来做你想做的事(听起来比实际更可怕)。使用模块的主要好处是,如果需要,您可以从外壳中卸载它们,并且它可以防止函数中的变量潜入外壳(一旦您点源函数文件,请尝试从在 shell 中运行,你会明白我的意思)。

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

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

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

Get-Module -listavailable

如果您看到一个名为 MyFunctions 的功能,那么您做对了,并且您的模块已准备好加载(这只是为了确保设置正确,您只需执行一次)。

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

Import-Module MyFunctions

您现在可以运行您的函数了。很酷的一点是,一旦你有 10-15 个函数,你就会忘记一对夫妇的名字。如果您将它们放在模块中,则可以运行以下命令来获取模块中所有函数的列表:

Get-Command -module MyFunctions

它非常可爱,而且在前端设置所需的一点点努力都是值得的。

【讨论】:

如果您的函数仅与给定的 PowerShell 应用程序相关怎么办?我的意思是,如果您在某处安装 PS1 软件包来完成工作,您可能不希望您的个人资料中的所有功能,对吧? 在这种情况下,我会为该特定应用程序创建一个模块,然后在运行脚本之前加载它(如果以交互方式工作),或者在脚本中加载它。但一般来说,如果您有特定于给定任务的代码,您会希望脚本中包含这些函数。就我个人而言,我只编写通常做一件事的函数。如果一段代码是高度专业化的,那么将它包装在一个函数或模块中是没有意义的(除非有几个脚本使用相同的代码,那么它可能是有意义的)。 模块文件不必位于与 PSM1 文件同名的文件夹中。可以像Import-Module .\buildsystem\PSUtils.psm1 @MichaelFreidgeim 如果它只是简单地将. 更改为Import-Module 并重命名扩展名,并且不需要将模块放置在特定文件夹中,即我可以拥有它在我想要的任何目录中,就像点源一样,考虑到范围界定带来的好处,是否有任何理由甚至对模块进行点源? (当然,除非那些范围“问题”是你想要的) @Abdul,点源更简单,模块更强大。见***.com/questions/14882332/…【参考方案3】:

. "$PSScriptRoot\MyFunctions.ps1" MyA1Func

从 v3 开始可用,在此之前请参阅 How can I get the file system location of a PowerShell script?。这是很常见的。

附:我不同意“一切都是模块”的规则。我的脚本被 GIT 之外的其他开发人员使用,所以我不喜欢在我的脚本运行之前将东西放在特定的地方或修改系统环境变量。这只是一个脚本(或两个,或三个)。

【讨论】:

FWIW,你不必为了在模块中运行脚本而做这两件事。 @NickCox 我很想看看这方面的一些例子。你有什么? +10 如果示例来自 OSS 项目。具体来说,通过相对路径(不是 PSModulePath 或没有自定义 PSModulePath)加载 PS 模块的示例,以及非平凡示例(即模块比普通脚本作用域更有优势的地方)。 我经常从相对路径导入FluentMigrator.PowerShell 模块。这让我们可以将它检查到源代码控制中,并确保每个人都使用相同的版本。效果很好。 我不确定将其打包为模块与脚本的相对优缺点:也许这是与作者讨论的问题?我想Get-Command -Module FluentMigrator.PowerShell 的可用性相当不错? @NickCox 您没有完全限定该命令中的模块路径,这意味着除非您将模块复制到全局模块文件夹或将 GIT 文件夹添加到全局环境变量。我想你只是证明了我的观点。【参考方案4】:

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

首先您需要检查以确保通过运行来加载函数:

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

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

【讨论】:

在 PowerShell 中函数被视为一个目录,因此它与 c:\ 或 d:\ 相同。同样,您可以在没有反斜杠的情况下使用 ls 函数:|其中 $_.Name -eq "A1" 【参考方案5】:

您可以将功能添加到:

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

该功能将可用。

【讨论】:

【参考方案6】:

如果您的文件只有一个您想要调用/公开的主要功能,那么您也可以使用以下方式开始文件:

Param($Param1)

然后您可以调用它,例如如下:

.\MyFunctions.ps1 -Param1 'value1'

如果您想轻松调用该函数而无需导入该函数,这将更加方便。

【讨论】:

我还应该注意,我今天发现(在我的一位同事告诉我之后)PowerShell 自动添加了[CmdletBinding()] 属性并将其免费升级为高级功能。 :-)【参考方案7】:

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

Import-Module "Dummy-Name.psm1";
Get-Command -Module "Function-Dumb";
#
#
Function-Dumb;

【讨论】:

以上是关于在 PowerShell 中,如何在文件中定义函数并从 PowerShell 命令行调用它?的主要内容,如果未能解决你的问题,请参考以下文章

如何调用自己写的python函数

从 C# 调用 Powershell 函数

如何在不大幅增加其大小的情况下在 PowerShell 中保存文本文件?

自定义Powershell提示符

如何在 PowerShell 中返回自定义错误级别?

如何递归获取PowerShell中的所有文件和文件夹的大小,自定义输出