如果我正在写"惯用" PowerShell,我将使用哪些参数类型用于处理文件或目录的函数?
例如,我制作了一个包含此功能的模块:
$bookmarks = @{}
$bookmarkKeys = @{}
function Set-Bookmark {
param(
[Parameter(Mandatory = $true)]
[string] $Id,
[System.Management.Automation.DirectoryInfo] $Path = (Get-Location))
$script:bookmarks[$Id] = $Path
$script:bookmarkKeys[$Path.Path] = $Id
}
麻烦的是,以下不起作用:
PS>Set-Bookmark -Id Code -Path C:\Code
Set-Bookmark : Cannot process argument transformation on parameter 'Path'.
Cannot convert the "C:\Code" value of type "System.String" to type
"System.Management.Automation.PathInfo".
At line:1 char:29
+ Set-Bookmark -Id Code -Path C:\Code
+ ~~~~~~~
+ CategoryInfo : InvalidData: (:) [Set-Bookmark], ParameterBindingArgumentTransformationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError,Set-Bookmark
奇怪的是,这也不是(但原因略有不同):
PS>Set-Bookmark -Id Code -Path (gi C:\Code)
Set-Bookmark : Cannot process argument transformation on parameter 'Path'.
Cannot convert the "C:\Code" value of type "System.IO.DirectoryInfo" to type
"System.Management.Automation.PathInfo".
At line:1 char:29
+ Set-Bookmark -Id Code -Path (gi C:\Code)
+ ~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Set-Bookmark], ParameterBindingArgumentTransformationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError,Set-Bookmark
那么我应该将我的参数实际定义为字符串(即最小公分母),然后尝试将它们转换/解析为更有意义的类型?这听起来不对,因为如果我处理Get-Item
之类的结果而感到羞耻,那么他们会因为传入而被降到string
,仅用于该函数再次解析为更高级别的类型。
答案 0 :(得分:4)
我认为你想要使用[IO.DirectoryInfo]
类型。这对我来说很好:
function t {
param(
[IO.DirectoryInfo] $d
)
"You passed $d"
}
t (Get-Item "C:\Windows")
对于文件,您可以使用[IO.FileInfo]
,但这不支持通配符。
答案 1 :(得分:1)
您在实际代码中使用了System.Management.Automation.PathInfo
类型,而不是System.Management.Automation.DirectoryInfo
,否则您将获得TypeNotFound
例外。 System.Management.Automation
命名空间中没有DirectoryInfo
个类。但是,正如@Bill_Stewart已经提到的那样,文件夹对象无论如何都是System.IO.DirectoryInfo
类型。 PathInfo
类适用于PowerShell路径,它们不一定是文件系统路径(例如,考虑证书或注册表provider)。
如果你想要一个可以同时接受文件(System.IO.FileInfo
)和文件夹(System.IO.DirectoryInfo
)的参数,你需要使参数类型成为两种类型的公共基类(例如{{3 }}):
function Set-Bookmark {
Param(
[Parameter(Mandatory=$true)]
[string] $Id,
[Parameter(Mandatory=$false)]
[IO.FileSystemInfo]$Path = (Get-Location)
)
...
}
或System.String
(因为文件和文件夹对象都可以转换为字符串,反之亦然):
function Set-Bookmark {
Param(
[Parameter(Mandatory=$true)]
[string] $Id,
[Parameter(Mandatory=$false)]
[ValidateScript({Test-Path -LiteralPath $_})]
[string]$Path = (Get-Location)
)
...
}
请注意,当使用String
作为-Path
参数的类型时,您需要自己System.IO.FileSystemInfo
参数以防止传递非有效路径的参数。另请注意,使用IO.FileSystemInfo
作为参数类型时,参数将不接受路径字符串(即Set-Bookmark -Id 42 -Path "C:\some\path"
将失败)。
底线:
惯用参数类型取决于您希望函数接受的输入。如果它的文件和文件夹对象使用System.IO.FileSystemInfo
。如果它的路径使用use System.String
,请验证输入,并根据需要将路径字符串(返回)转换为文件/文件夹对象。
答案 2 :(得分:1)
从可用性的角度来看,我总是发现将文件和目录参数声明为类型string
更容易。 PowerShell如此灵活的原因在于能够结合来自不同来源的功能。我不知道您的目标受众是谁,但假设用户想要在Set-Bookmark
中为文本文件中的条目调用。如果它接受了一个字符串,那么调用该函数要简单得多:
$id = 0
Get-Content 'bookmarks.txt' | For-EachObject { Set-Bookmark -id $i++ -Path $_ }
还有一个事实是您可能并不总是使用文件提供程序,因此假设您的Path始终表示文件路径将限制此函数的有用性。例如,如果您使用的是Microsoft SQL提供程序,则可能希望您的函数为SQL提供程序中的路径提供书签。 (使用Test-Path
的第二个解决方案可行。)同样,我不了解您的目标受众,但值得考虑。