Ich würde empfehlen Argument Beender verwenden, die in Powershell halb ausgesetzt sind 3 und 4, und voll in der Version 5.0 und höher ausgesetzt. Für v3 und v4 ist die zugrunde liegende Funktionalität vorhanden, Sie müssen jedoch die integrierte TabExpansion2-Funktion überschreiben, um sie zu verwenden. Das ist in Ordnung für Ihre eigene Sitzung, aber es ist in der Regel verpönt, Tools zu verteilen, die das in Sitzungen anderer Leute tun (stellen Sie sich vor, wenn jeder versucht, diese Funktion zu überschreiben). Ein PowerShell-Teammitglied verfügt über ein Modul, das dies für Sie erledigt: TabExpansionPlusPlus. Ich weiß, ich sagte, TabExpansion2 war schlecht, aber es ist OK, wenn dieses Modul es tut :)
Wenn ich die Versionen 3 und 4 unterstützen musste, würde ich meine Befehle in Modulen verteilen und die Module auf die Existenz von überprüfen lassen der Befehl 'Register-ArgumentCompleter', der ein Cmdlet in Version 5 + ist und eine Funktion für das TE ++ - Modul ist. Wenn das Modul es gefunden hat, würde es alle Completer registrieren, und wenn dies nicht der Fall wäre, würde es den Benutzer darüber informieren, dass die Argumentvervollständigung nicht funktionieren würde, wenn sie nicht das Modul TabExpansionPlusPlus erhalten hätten.
Vorausgesetzt, dass Sie die TE ++ Modul oder pSV5 + haben, ich denke, das sollten Sie sich auf dem richtigen Weg:
function launcher {
[CmdletBinding()]
param(
[string] $Environment1,
[string] $Environment2,
[string] $Environment3
)
$PSBoundParameters
}
1..3 | ForEach-Object {
Register-ArgumentCompleter -CommandName launcher -ParameterName "Environment${_}" -ScriptBlock {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
$PathParts = $fakeBoundParameter.Keys | where { $_ -like 'Environment*' } | sort | ForEach-Object {
$fakeBoundParameter[$_]
}
Get-ChildItem -Path ".\configurations\$($PathParts -join '\')" -Directory -ErrorAction SilentlyContinue | select -ExpandProperty Name | where { $_ -like "${wordToComplete}*" } | ForEach-Object {
New-Object System.Management.Automation.CompletionResult (
$_,
$_,
'ParameterValue',
$_
)
}
}
}
Damit dies funktioniert, Ihr aktuelles Arbeitsverzeichnis ein Verzeichnis darin enthaltenen ‚Konfigurationen‘ müssen , und Sie werden mindestens drei Ebenen von Unterverzeichnissen benötigen (wenn Sie Ihr Beispiel durchlesen, sah es so aus, als ob Sie ein Verzeichnis aufzählen würden, und Sie würden tiefer in diese Struktur einsteigen, wenn Parameter hinzugefügt wurden). Die Aufzählung des Verzeichnisses ist im Moment nicht sehr intelligent, und Sie können es ziemlich einfach täuschen, wenn Sie einfach einen Parameter überspringen, z. B. launcher -Environment3 <TAB>
würde versuchen, Ihnen Komplettierungen für das erste Unterverzeichnis zu geben.
Dies funktioniert, wenn Sie immer drei Parameter zur Verfügung haben. Wenn Sie eine variable Anzahl von Parametern benötigen, können Sie noch Completers verwenden, aber es könnte etwas komplizierter werden.
Der größte Nachteil wäre, dass Sie die Eingaben der Benutzer noch validieren müssen, da es sich bei den Completers im Grunde nur um Vorschläge handelt und die Benutzer diese Vorschläge nicht verwenden müssen.
Wenn Sie dynamische Parameter verwenden möchten, wird es ziemlich verrückt. Es mag einen besseren Weg geben, aber ich war noch nie in der Lage, den Wert von dynamischen Parametern in der Befehlszeile zu sehen, ohne Reflektion zu verwenden, und an diesem Punkt verwenden Sie Funktionen, die sich bei der nächsten Version ändern könnten (die Mitglieder sind normalerweise t öffentlich aus einem Grund). Es ist verlockend zu versuchen, $ MyInvocation im Block DynamicParam {} zu verwenden, aber zu dem Zeitpunkt, zu dem der Benutzer den Befehl in die Befehlszeile eingibt, wird er nicht ausgefüllt, und es wird nur eine Zeile des Befehls angezeigt, ohne dass eine Spiegelung verwendet wird.
Der untenstehende wurde auf PowerShell 5.1 getestet, so kann ich nicht garantieren, dass jede andere Version genau dieselben Klassenmitglieder hat (es basiert auf etwas, das ich zuerst sah Garrett Serack tun). Wie im vorherigen Beispiel hängt es von einem. \ Configurations-Ordner im aktuellen Arbeitsverzeichnis ab (wenn es keins gibt, werden Sie keine -Environment-Parameter sehen).
function badlauncher {
[CmdletBinding()]
param()
DynamicParam {
#region Get the arguments
# In it's current form, this will ignore parameter names, e.g., '-ParameterName ParameterValue' would ignore '-ParameterName',
# and only 'ParameterValue' would be in $UnboundArgs
$BindingFlags = [System.Reflection.BindingFlags] 'Instance, NonPublic, Public'
$Context = $PSCmdlet.GetType().GetProperty('Context', $BindingFlags).GetValue($PSCmdlet)
$CurrentCommandProcessor = $Context.GetType().GetProperty('CurrentCommandProcessor', $BindingFlags).GetValue($Context)
$ParameterBinder = $CurrentCommandProcessor.GetType().GetProperty('CmdletParameterBinderController', $BindingFlags).GetValue($CurrentCommandProcessor)
$UnboundArgs = @($ParameterBinder.GetType().GetProperty('UnboundArguments', $BindingFlags).GetValue($ParameterBinder) | where { $_ } | ForEach-Object {
try {
if (-not $_.GetType().GetProperty('ParameterNameSpecified', $BindingFlags).GetValue($_)) {
$_.GetType().GetProperty('ArgumentValue', $BindingFlags).GetValue($_)
}
}
catch {
# Don't do anything??
}
})
#endregion
$ParamDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
# Create an Environment parameter for each argument specified, plus one extra as long as there
# are valid subfolders under .\configurations
for ($i = 0; $i -le $UnboundArgs.Count; $i++) {
$ParameterName = "Environment$($i + 1)"
$ParamAttributes = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$ParamAttributes.Add((New-Object Parameter))
$ParamAttributes[0].Position = $i
# Build the path that will be enumerated based on previous arguments
$PathSb = New-Object System.Text.StringBuilder
$PathSb.Append('.\configurations\') | Out-Null
for ($j = 0; $j -lt $i; $j++) {
$PathSb.AppendFormat('{0}\', $UnboundArgs[$j]) | Out-Null
}
$ValidParameterValues = Get-ChildItem -Path $PathSb.ToString() -Directory -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name
if ($ValidParameterValues) {
$ParamAttributes.Add((New-Object ValidateSet $ValidParameterValues))
$ParamDictionary[$ParameterName] = New-Object System.Management.Automation.RuntimeDefinedParameter (
$ParameterName,
[string[]],
$ParamAttributes
)
}
}
return $ParamDictionary
}
process {
$PSBoundParameters
}
}
Die kühle Sache über dieses ist, dass es so lange weitermachen können, da es Ordner sind, und es funktioniert automatisch Parametervalidierung. Natürlich brechen Sie die .NET-Gesetze, indem Sie Reflektionen nutzen, um all diese privaten Mitglieder zu erreichen, also würde ich das als eine schreckliche und zerbrechliche Lösung betrachten, egal wie lustig es war, sich zu entschließen.
Wissen Sie, welche Version von PowerShell-Benutzern der Funktion haben wird? Ich würde die Verwendung von Argument-Completers empfehlen, die mit Version 5+ geliefert werden und für 3 und 4 mit dem TabExpansionPlusPlus-Modul verfügbar sind. –
Powershell v3.0 und ich haben keinen administrativen Zugriff. Außerdem würde ich lieber genau das umsetzen, was ich von irgendwelchen Bibliotheken benötige, da die Erlaubnis, etwas Äußeres zu verwenden, ziemlich begrenzt ist. – oblio