Esta es un área sorprendentemente compleja, pero tengo mucha experiencia aquí. En resumen, hay algunos cmdlets que aceptan rutas win32 directamente desde las API de System.IO, y estas normalmente usan un parámetro -FilePath. Si desea escribir un cmdlet "powershelly" bien gestionado, necesita -Path y -LiteralPath, para aceptar la entrada de canalización y trabajar con rutas de proveedor relativas y absolutas. He aquí un extracto de un post que escribí hace un tiempo: [. En un principio]
Caminos en PowerShell son difíciles de entender PowerShell Caminos - o PSPaths, que no debe confundirse con las rutas de Win32 - en sus formas absolutas, vienen en dos sabores distintos:
- proveedor cualificado:
FileSystem::c:\temp\foo.txt
- PSDrive cualificado:
c:\temp\foo.txt
Es muy fácil confundirse sobre provider-internal (La propiedad ProviderPath
de System.Management.Automation.PathInfo
resuelto - la parte a la derecha de ::
de la ruta calificada por el proveedor anterior) y las rutas calificadas por la unidad ya que tienen el mismo aspecto si mira las unidades de proveedor de FileSystem predeterminadas. Es decir, el PSDrive tiene el mismo nombre (C) que el almacén de respaldo nativo, el sistema de archivos de Windows (C). Por lo tanto, para que sea más fácil para usted para entender las diferencias, crear usted mismo una nueva PSDrive:
ps c:\> new-psdrive temp filesystem c:\temp\
ps c:\> cd temp:
ps temp:\>
Ahora, vamos a ver esto de nuevo:
- proveedor cualificado:
FileSystem::c:\temp\foo.txt
- Drive- calificado:
temp:\foo.txt
Es un poco más fácil esta vez para ver qué es diferente esta vez. El texto en negrita a la derecha del nombre del proveedor es ProviderPath.
Por lo tanto, sus objetivos para escribir un cmdlet generalizada proveedor de usar (o de funciones avanzadas) que acepte caminos son:
- definir un parámetro
LiteralPath
ruta alias a PSPath
- definir un parámetro
Path
(que se resolver los comodines/glob)
- siempre ha de asumir que está recibiendo PSPaths, NO proveedor senderos nativos (por ejemplo, caminos Win32)
El punto número tres es especialmente importante. Además, obviamente LiteralPath
y Path
deben pertenecer a conjuntos de parámetros mutuamente excluyentes.
rutas relativas
Una buena pregunta es: ¿Cómo tratamos con rutas relativas que se pasa a un cmdlet. A medida que se debe asumir todos los caminos están dando a usted es PSPaths, vamos a ver a continuación lo hace el cmdlet:
ps temp:\> write-zip -literalpath foo.txt
El comando debe asumir foo.txt se encuentra en la unidad actual, por lo que este debe ser resuelto de inmediato en el ProcessRecord o EndProcessing bloque como (usando la API de secuencias de comandos aquí para demostración):
$provider = $null;
$drive = $null
$pathHelper = $ExecutionContext.SessionState.Path
$providerPath = $pathHelper.GetUnresolvedProviderPathFromPSPath(
"foo.txt", [ref]$provider, [ref]$drive)
Ahora todo lo necesario para recrear las dos formas absolutas de PSPaths, y también tiene la ProviderPath absoluta nativa. Para crear un PSPath calificado por el proveedor para foo.txt, use $provider.Name + “::” + $providerPath
. Si $drive
no es $null
(su ubicación actual puede ser un proveedor calificado en cuyo caso $drive
será $null
) entonces debe usar $drive.name + ":\" + $drive.CurrentLocation + "\" + "foo.txt"
para obtener un PSPath calificado para el controlador.
de inicio rápido C# Esqueleto
Aquí está un esqueleto de un cmdlet C# proveedor de cuenta para que te va. Ha incorporado comprobaciones para garantizar que se le haya entregado una ruta de proveedor de FileSystem.Estoy en el proceso de envasado de esto para NuGet para ayudar a otros a obtener la escritura de buen comportamiento cmdlets proveedor consciente:
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;
}
}
}
Directrices para el desarrollo de cmdlet (Microsoft)
He aquí algunos consejos más generalizada que serle de ayuda en el largo plazo: http://msdn.microsoft.com/en-us/library/ms714657%28VS.85%29.aspx
Pista: siempre debe obtener el SessionState del ExecutionContext del Cmdlet. – x0n
Parece que está buscando [PSCmdlet.GetUnresolvedProviderPathFromPSPath Method] (http://msdn.microsoft.com/en-us/library/system.management.automation.pscmdlet.getunresolvedproviderpathfpspspath (v = VS.85) .aspx) –
Esta fue mi salvación. ¡Saludo! – Simon