Đây là khu vực phức tạp đáng ngạc nhiên, nhưng tôi có rất nhiều kinh nghiệm ở đây. Tóm lại, có một số lệnh ghép ngắn chấp nhận đường dẫn win32 trực tiếp từ API System.IO, và thường sử dụng tham số -FilePath. Nếu bạn muốn viết một lệnh "powershelly" tốt, bạn cần -Path và -LiteralPath, để chấp nhận đầu vào đường ống và làm việc với các đường dẫn nhà cung cấp tương đối và tuyệt đối. Dưới đây là một đoạn trích từ một bài đăng trên blog mà tôi đã viết cách đây không lâu:
Đường dẫn trong PowerShell khó hiểu [lúc đầu]. họ có hai hương vị riêng biệt:
- Provider-trình độ:
FileSystem::c:\temp\foo.txt
- PSDrive trình độ:
c:\temp\foo.txt
Nó rất dễ dàng để bị lẫn lộn trên provider-internal (thuộc tính ProviderPath
của một giải quyết System.Management.Automation.PathInfo
- phần bên phải của ::
của đường dẫn đủ điều kiện của nhà cung cấp ở trên) và đường dẫn đủ điều kiện vì chúng trông giống nhau nếu bạn nhìn vào ổ đĩa nhà cung cấp hệ thống FileSystem mặc định. Tức là, PSDrive có cùng tên (C) làm cửa hàng sao lưu gốc, hệ thống tệp windows (C). Vì vậy, để làm cho nó dễ dàng hơn cho chính mình để hiểu được sự khác biệt, tạo cho mình một PSDrive mới:
ps c:\> new-psdrive temp filesystem c:\temp\
ps c:\> cd temp:
ps temp:\>
Bây giờ, chúng ta hãy xem xét điều này một lần nữa:
- Provider-trình độ:
FileSystem::c:\temp\foo.txt
- Drive- đủ điều kiện:
temp:\foo.txt
Lần này dễ dàng hơn để xem thời điểm này khác. Văn bản in đậm ở bên phải của tên nhà cung cấp là ProviderPath.
Vì vậy, mục tiêu của mình để viết một lệnh tổng quát cung cấp dịch vụ thân thiện (hoặc chức năng nâng cao) chấp nhận đường dẫn là:
- Xác định một tham số
LiteralPath
đường aliased để PSPath
- Xác định một tham số
Path
(mà sẽ giải quyết các ký tự đại diện/glob)
- Luôn giả sử bạn đang nhận PSPath, KHÔNG phải đường dẫn nhà cung cấp gốc (ví dụ: đường dẫn Win32)
Điểm số ba đặc biệt quan trọng. Ngoài ra, rõ ràng là LiteralPath
và Path
phải thuộc về bộ tham số loại trừ lẫn nhau.
Paths tương đối
Một câu hỏi tốt là: làm thế nào để chúng ta đối phó với đường dẫn tương đối được truyền cho một lệnh. Như bạn nên cho tất cả các đường dẫn được trao cho bạn là PSPaths, chúng ta hãy nhìn vào những gì các Cmdlet dưới đây không:
ps temp:\> write-zip -literalpath foo.txt
Lệnh nên cho foo.txt là trong ổ đĩa hiện tại, vì thế này nên được giải quyết ngay lập tức trong ProcessRecord hoặc EndProcessing khối tương tự (sử dụng API scripting đây để demo):
$provider = $null;
$drive = $null
$pathHelper = $ExecutionContext.SessionState.Path
$providerPath = $pathHelper.GetUnresolvedProviderPathFromPSPath(
"foo.txt", [ref]$provider, [ref]$drive)
Bây giờ bạn có tất cả mọi thứ bạn cần phải tạo hai hình thức tuyệt đối của PSPaths, và bạn cũng có ProviderPath tuyệt đối tự nhiên. Để tạo PSPath đủ điều kiện cho nhà cung cấp cho foo.txt, hãy sử dụng $provider.Name + “::” + $providerPath
. Nếu $drive
không phải là $null
(vị trí hiện tại của bạn có thể là nhà cung cấp đủ điều kiện trong trường hợp $drive
sẽ là $null
) thì bạn nên sử dụng $drive.name + ":\" + $drive.CurrentLocation + "\" + "foo.txt"
để có PSPath đủ điều kiện lái xe.
Quickstart C# Skeleton
Dưới đây là một bộ xương của một C# cung cấp-aware cmdlet để giúp bạn đi. Nó đã được xây dựng trong kiểm tra để đảm bảo nó đã được giao một đường dẫn nhà cung cấp FileSystem.Tôi đang trong quá trình đóng gói này lên cho NuGet để giúp đỡ người khác nhận bằng văn bản well-behaved Cmdlets nhà cung cấp-aware:
using System;
using System.Collections.Generic;
using System.IO;
using System.Management.Automation;
using Microsoft.PowerShell.Commands;
namespace PSQuickStart
{
[Cmdlet(VerbsCommon.Get, Noun,
DefaultParameterSetName = ParamSetPath,
SupportsShouldProcess = true)
]
public class GetFileMetadataCommand : PSCmdlet
{
private const string Noun = "FileMetadata";
private const string ParamSetLiteral = "Literal";
private const string ParamSetPath = "Path";
private string[] _paths;
private bool _shouldExpandWildcards;
[Parameter(
Position = 0,
Mandatory = true,
ValueFromPipeline = false,
ValueFromPipelineByPropertyName = true,
ParameterSetName = ParamSetLiteral)
]
[Alias("PSPath")]
[ValidateNotNullOrEmpty]
public string[] LiteralPath
{
get { return _paths; }
set { _paths = value; }
}
[Parameter(
Position = 0,
Mandatory = true,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
ParameterSetName = ParamSetPath)
]
[ValidateNotNullOrEmpty]
public string[] Path
{
get { return _paths; }
set
{
_shouldExpandWildcards = true;
_paths = value;
}
}
protected override void ProcessRecord()
{
foreach (string path in _paths)
{
// This will hold information about the provider containing
// the items that this path string might resolve to.
ProviderInfo provider;
// This will be used by the method that processes literal paths
PSDriveInfo drive;
// this contains the paths to process for this iteration of the
// loop to resolve and optionally expand wildcards.
List<string> filePaths = new List<string>();
if (_shouldExpandWildcards)
{
// Turn *.txt into foo.txt,foo2.txt etc.
// if path is just "foo.txt," it will return unchanged.
filePaths.AddRange(this.GetResolvedProviderPathFromPSPath(path, out provider));
}
else
{
// no wildcards, so don't try to expand any * or ? symbols.
filePaths.Add(this.SessionState.Path.GetUnresolvedProviderPathFromPSPath(
path, out provider, out drive));
}
// ensure that this path (or set of paths after wildcard expansion)
// is on the filesystem. A wildcard can never expand to span multiple
// providers.
if (IsFileSystemPath(provider, path) == false)
{
// no, so skip to next path in _paths.
continue;
}
// at this point, we have a list of paths on the filesystem.
foreach (string filePath in filePaths)
{
PSObject custom;
// If -whatif was supplied, do not perform the actions
// inside this "if" statement; only show the message.
//
// This block also supports the -confirm switch, where
// you will be asked if you want to perform the action
// "get metadata" on target: foo.txt
if (ShouldProcess(filePath, "Get Metadata"))
{
if (Directory.Exists(filePath))
{
custom = GetDirectoryCustomObject(new DirectoryInfo(filePath));
}
else
{
custom = GetFileCustomObject(new FileInfo(filePath));
}
WriteObject(custom);
}
}
}
}
private PSObject GetFileCustomObject(FileInfo file)
{
// this message will be shown if the -verbose switch is given
WriteVerbose("GetFileCustomObject " + file);
// create a custom object with a few properties
PSObject custom = new PSObject();
custom.Properties.Add(new PSNoteProperty("Size", file.Length));
custom.Properties.Add(new PSNoteProperty("Name", file.Name));
custom.Properties.Add(new PSNoteProperty("Extension", file.Extension));
return custom;
}
private PSObject GetDirectoryCustomObject(DirectoryInfo dir)
{
// this message will be shown if the -verbose switch is given
WriteVerbose("GetDirectoryCustomObject " + dir);
// create a custom object with a few properties
PSObject custom = new PSObject();
int files = dir.GetFiles().Length;
int subdirs = dir.GetDirectories().Length;
custom.Properties.Add(new PSNoteProperty("Files", files));
custom.Properties.Add(new PSNoteProperty("Subdirectories", subdirs));
custom.Properties.Add(new PSNoteProperty("Name", dir.Name));
return custom;
}
private bool IsFileSystemPath(ProviderInfo provider, string path)
{
bool isFileSystem = true;
// check that this provider is the filesystem
if (provider.ImplementingType != typeof(FileSystemProvider))
{
// create a .NET exception wrapping our error text
ArgumentException ex = new ArgumentException(path +
" does not resolve to a path on the FileSystem provider.");
// wrap this in a powershell errorrecord
ErrorRecord error = new ErrorRecord(ex, "InvalidProvider",
ErrorCategory.InvalidArgument, path);
// write a non-terminating error to pipeline
this.WriteError(error);
// tell our caller that the item was not on the filesystem
isFileSystem = false;
}
return isFileSystem;
}
}
}
Hướng dẫn phát triển Cmdlet (Microsoft)
Dưới đây là một số lời khuyên tổng quát hơn mà sẽ giúp bạn ra ngoài trong thời gian dài: http://msdn.microsoft.com/en-us/library/ms714657%28VS.85%29.aspx
Gợi ý: bạn nên luôn lấy SessionState từ ExecutionContext của Cmdlet. – x0n
Có vẻ như bạn đang tìm kiếm [Phương thức PSCmdlet.GetUnresolvedProviderPathFromPSPath] (http://msdn.microsoft.com/en-us/library/system.management.automation.pscmdlet.getunresolvedproviderpathfrompspath (v = VS.85) .aspx) –
Đây là sự cứu rỗi của tôi. Chào! – Simon