编写 C# cmdlet 时接收文件作为参数的正确方法是什么?到目前为止,我只有一个属性 LiteralPath (与它们的参数命名约定一致),它是一个字符串。这是一个问题,因为你只能得到在控制台中输入的任何内容;这可以是完整路径,也可以是相对路径。
使用 Path.GetFullPath(string) 不起作用。它认为我目前处于〜,但我不是。如果我将属性从字符串更改为 FileInfo,也会出现同样的问题。
编辑:对于任何感兴趣的人,此解决方法对我有用:
SessionState ss = new SessionState();
Directory.SetCurrentDirectory(ss.Path.CurrentFileSystemLocation.Path);
LiteralPath = Path.GetFullPath(LiteralPath);
LiteralPath 是字符串参数。我仍然有兴趣了解处理作为参数传递的文件路径的推荐方法。
EDIT2:这更好,这样你就不会弄乱用户当前目录,你应该将其设置回来。
string current = Directory.GetCurrentDirectory();
Directory.SetCurrentDirectory(ss.Path.CurrentFileSystemLocation.Path);
LiteralPath = Path.GetFullPath(LiteralPath);
Directory.SetCurrentDirectory(current);
What is the proper way to receive a file as a parameter when writing a C# cmdlet? So far I just have a property LiteralPath (aligning with their parameter naming convention) that is a string. This is a problem because you just get whatever is typed into the console; which could be the full path or could be a relative path.
Using Path.GetFullPath(string) doesn't work. It thinks I'm currently at ~, I'm not. Same problem occurs if I change the property from a string to a FileInfo.
EDIT: For anyone interested, this workaround is working for me:
SessionState ss = new SessionState();
Directory.SetCurrentDirectory(ss.Path.CurrentFileSystemLocation.Path);
LiteralPath = Path.GetFullPath(LiteralPath);
LiteralPath is the string parameter. I'm still interested in learning what is the recommended way to handle file paths that are passed as parameters.
EDIT2: This is better, so that you don't mess with the users current directory, you should set it back.
string current = Directory.GetCurrentDirectory();
Directory.SetCurrentDirectory(ss.Path.CurrentFileSystemLocation.Path);
LiteralPath = Path.GetFullPath(LiteralPath);
Directory.SetCurrentDirectory(current);
发布评论
评论(1)
这是一个令人惊讶的复杂领域,但我在这里有丰富的经验。简而言之,有一些 cmdlet 直接从 System.IO API 接受 win32 路径,并且这些 cmdlet 通常使用 -FilePath 参数。如果您想编写一个行为良好的“powershelly”cmdlet,则需要 -Path 和 -LiteralPath,以接受管道输入并使用相对和绝对提供程序路径。以下是我不久前写的博客文章的摘录:
PowerShell 中的路径最初很难理解。PowerShell 路径 - 或 PSPaths,不要与 Win32 路径混淆 - 以其绝对形式,它们有两种不同的风格:
FileSystem::c:\temp\foo.txt
c:\temp\foo.txt
PSDrive 很容易对提供程序内部感到困惑(已解析的
System.Management.Automation.PathInfo
的ProviderPath
属性 -::< 右侧的部分/code> 上面的提供程序限定路径)和驱动器限定路径,因为如果您查看默认的文件系统提供程序驱动器,它们看起来是相同的。也就是说,PSDrive 与本机后备存储 Windows 文件系统 (C) 具有相同的名称 (C)。因此,为了让您自己更容易理解差异,请为自己创建一个新的 PSDrive:
现在,让我们再看一下:
FileSystem::c:\temp\foo.txt
temp:\foo.txt
这次更容易看出这次有什么不同。提供程序名称右侧的粗体文本是提供程序路径。
因此,编写接受路径的通用提供者友好型 Cmdlet(或高级函数)的目标是:
PSPath
的LiteralPath
路径参数Path
参数(将解析通配符/全局变量)第三点尤其重要。此外,显然
LiteralPath
和Path
应该属于互斥的参数集。相对路径
一个很好的问题是:我们如何处理传递给 Cmdlet 的相对路径。由于您应该假设提供给您的所有路径都是 PSPaths,所以让我们看看下面的 Cmdlet 的作用:
该命令应假设 foo.txt 位于当前驱动器中,因此应立即在 ProcessRecord 或 EndProcessing 块中解决此问题,例如(使用此处的脚本 API 进行演示):
现在您已拥有重新创建 PSPath 的两种绝对形式所需的一切,并且您还拥有本机绝对 ProviderPath。要为 foo.txt 创建提供者限定的 PSPath,请使用
$provider.Name + “::” + $providerPath
。如果$drive
不是$null
(您当前的位置可能是提供商限定的,在这种情况下$drive
将为$null< /code>) 那么您应该使用
$drive.name + ":\" + $drive.CurrentLocation + "\" + "foo.txt"
来获取驱动器限定的 PSPath。快速入门 C# 骨架
下面是一个 C# 提供程序感知 cmdlet 的骨架,可帮助您入门。它内置了检查,以确保它已获得文件系统提供程序路径。我正在将其打包为 NuGet,以帮助其他人编写行为良好的提供程序感知 Cmdlet:
Cmdlet 开发指南 (Microsoft)
以下是一些更通用的建议,可以帮助您解决以下问题:长远来看:
https://learn.microsoft.com/en-us/powershell/scripting/developer/cmdlet/strongly-encouraging-development-guidelines?view=powershell-7.4#support-windows-powershell-paths
This is a surprisingly complex area, but I have a ton of experience here. In short, there are some cmdlets that accept win32 paths straight from the System.IO APIs, and these typically use a -FilePath parameter. If you want to write a well behaved "powershelly" cmdlet, you need -Path and -LiteralPath, to accept pipeline input and work with relative and absolute provider paths. Here's an excerpt from a blog post I wrote a while ago:
Paths in PowerShell are tough to understand [at first.] PowerShell Paths - or PSPaths, not to be confused with Win32 paths - in their absolute forms, they come in two distinct flavours:
FileSystem::c:\temp\foo.txt
c:\temp\foo.txt
It's very easy to get confused over provider-internal (The
ProviderPath
property of a resolvedSystem.Management.Automation.PathInfo
– the portion to the right of::
of the provider-qualified path above) and drive-qualified paths since they look the same if you look at the default FileSystem provider drives. That is to say, the PSDrive has the same name (C) as the native backing store, the windows filesystem (C). So, to make it easier for yourself to understand the differences, create yourself a new PSDrive:Now, let's look at this again:
FileSystem::c:\temp\foo.txt
temp:\foo.txt
A bit easier this time to see what’s different this time. The bold text to the right of the provider name is the ProviderPath.
So, your goals for writing a generalized provider-friendly Cmdlet (or advanced function) that accepts paths are:
LiteralPath
path parameter aliased toPSPath
Path
parameter (which will resolve wildcards / glob)Point number three is especially important. Also, obviously
LiteralPath
andPath
should belong in mutually exclusive parameter sets.Relative Paths
A good question is: how do we deal with relative paths being passed to a Cmdlet. As you should assume all paths being given to you are PSPaths, let’s look at what the Cmdlet below does:
The command should assume foo.txt is in the current drive, so this should be resolved immediately in the ProcessRecord or EndProcessing block like (using the scripting API here to demo):
Now you everything you need to recreate the two absolute forms of PSPaths, and you also have the native absolute ProviderPath. To create a provider-qualified PSPath for foo.txt, use
$provider.Name + “::” + $providerPath
. If$drive
is not$null
(your current location might be provider-qualified in which case$drive
will be$null
) then you should use$drive.name + ":\" + $drive.CurrentLocation + "\" + "foo.txt"
to get a drive-qualified PSPath.Quickstart C# Skeleton
Here's a skeleton of a C# provider-aware cmdlet to get you going. It has built in checks to ensure it has been handed a FileSystem provider path. I am in the process of packaging this up for NuGet to help others get writing well-behaved provider-aware Cmdlets:
Cmdlet Development Guidelines (Microsoft)
Here is some more generalized advice that should help you out in the long run:
https://learn.microsoft.com/en-us/powershell/scripting/developer/cmdlet/strongly-encouraged-development-guidelines?view=powershell-7.4#support-windows-powershell-paths