Cómo usar ArgumentCompleter y Register-ArgumentCompleter en PowerShell

Última actualización: 17/12/2025
Autor: Isaac
  • Register-ArgumentCompleter permite crear completadores dinámicos para parámetros de cmdlets y comandos nativos usando ScriptBlock y objetos CompletionResult.
  • Los atributos ValidateSet, ArgumentCompletions y ArgumentCompleter ofrecen niveles crecientes de flexibilidad para definir listas de valores sugeridos o validados.
  • PowerShell 7.2 introduce completadores basados en clases y fábricas que facilitan la reutilización y parametrización de lógicas de autocompletado complejas.
  • La combinación de PSReadLine, completadores personalizados y soporte para CLIs y APIs externas acerca la experiencia de autocompletado de PowerShell a la de shells avanzados como zsh.

cómo ver el historial de comandos en Powershell y CMD

En las próximas líneas vamos a ver, con calma y con ejemplos claros, cómo usar Register-ArgumentCompleter, los atributos ArgumentCompleter, ArgumentCompletions, ValidateSet y las clases personalizadas para conseguir una experiencia de autocompletado muy avanzada en PowerShell. Verás tanto casos simples como escenarios bastante más sofisticados, incluyendo cómo integrarlo en tu perfil de PowerShell para tenerlo siempre activo y cómo usarlo también con comandos nativos como dotnet o CLIs de terceros.

Qué es Register-ArgumentCompleter y qué problema resuelve

powershell
Artículo relacionado:
Cómo dominar el historial de fiabilidad en PowerShell: gestión, auditoría y análisis

El cmdlet Register-ArgumentCompleter sirve para registrar un completador de argumentos personalizado. En otras palabras, le dices a PowerShell qué debe sugerir cuando el usuario pulsa la tecla Tab al rellenar el valor de un parámetro o de un comando nativo.

Este completador se ejecuta en tiempo de ejecución, así que puede calcular las sugerencias de forma dinámica: consultar cmdlets como Get-TimeZone, filtrar servicios con Get-Service, llamar a una CLI como dotnet complete, leer un API REST, revisar el sistema de archivos, etc. Todo se hace a través de un ScriptBlock que devuelve los posibles valores de autocompletado.

Un detalle importante es cómo se comporta el parámetro CommandName: si lo usas sin indicar ParameterName ni Native, PowerShell trata la llamada como si hubieras añadido -Native. Eso significa que el completador no funcionará sobre parámetros de cmdlets de PowerShell, sino sólo como completador para comandos nativos. Por eso, si tu objetivo es completar parámetros de un cmdlet o función de PowerShell, debes especificar siempre ParameterName.

Sintaxis básica de Register-ArgumentCompleter

Conjunto de parámetros para comandos nativos (NativeSet)

Para registrar un completador de argumentos para un comando nativo, se usa la siguiente sintaxis, donde la clave está en el modificador -Native:

Sintaxis típica: Register-ArgumentCompleter -CommandName <String[]> -ScriptBlock <ScriptBlock> -Native

En este caso CommandName es obligatorio, porque PowerShell debe saber a qué comando nativo asociar el completador. El ScriptBlock recibirá un conjunto de parámetros distinto al de los completadores para cmdlets de PowerShell, como veremos más adelante, y estará orientado a interpretar la línea completa y la posición del cursor.

Conjunto de parámetros para cmdlets de PowerShell (PowerShellSet)

Si lo que quieres es añadir autocompletado a uno o varios parámetros de cmdlets o funciones de PowerShell, la sintaxis más habitual es:

Sintaxis típica: Register-ArgumentCompleter -ParameterName <String> -ScriptBlock <ScriptBlock> >]

Aquí ParameterName es obligatorio y define sobre qué parámetro se aplicará la finalización. El parámetro CommandName es opcional: puedes limitar el completador a uno o varios comandos concretos o, si lo omites, PowerShell registrará ese completador para ese nombre de parámetro en todos los comandos que lo tengan.

Parámetros clave de Register-ArgumentCompleter

Parámetro -CommandName

El parámetro -CommandName indica para qué comando o comandos se va a registrar el completador. Acepta un array de cadenas, por lo que puedes asociar el mismo ScriptBlock a varios cmdlets o herramientas nativas al mismo tiempo.

Cuando se usa en el conjunto de parámetros NativeSet, es obligatorio, ya que los completadores nativos no se pueden registrar de forma genérica sin apuntar a un binario o comando concreto. En el conjunto PowerShellSet es opcional, pero, como se comentaba antes, si lo usas sin ParameterName ni Native, PowerShell interpretará que estás creando un completador nativo.

Parámetro -Native

El modificador -Native le dice a PowerShell que el completador se debe aplicar a comandos nativos, es decir, ejecutables externos donde PowerShell no controla ni puede completar los nombres de parámetros. Pensemos en dotnet, winget, aws o cualquier otra CLI basada en .NET, Python o similar.

En este modo, el ScriptBlock recibe menos parámetros y con otro propósito: el texto que se ha escrito, el AST del comando y la posición del cursor. A partir de ahí normalmente el script llama a la propia CLI para que devuelva sugerencias, como ocurre con dotnet complete.

Parámetro -ParameterName

El parámetro -ParameterName marca el nombre del parámetro al que se le va a aplicar la finalización de argumentos. Hay una limitación importante: el tipo de ese parámetro no puede ser una enumeración. Por ejemplo, si el parámetro utiliza un tipo como el ForegroundColor de Write-Host, no puedes utilizar Register-ArgumentCompleter para completarlo.

  Cómo crear tu propio instalador para programas de Windows

Cuando registras completadores para cmdlets o funciones de PowerShell, es altamente recomendable que siempre utilices ParameterName. Si te olvidas y sólo usas CommandName, acabarás registrando sin querer un completador nativo y no verás las sugerencias en el parámetro que te interesa.

Parámetro -ScriptBlock

El corazón de cualquier completador es el ScriptBlock. Este bloque de script define la lógica que genera los valores a sugerir cuando el usuario pulsa Tab. Lo más importante es que el ScriptBlock debe devolver los elementos mediante la canalización, por ejemplo, con ForEach-Object o Where-Object, en lugar de devolver un único array, porque si se devuelve una matriz sin canalizar, PowerShell la interpretará como un solo valor de autocompletado.

Además, el ScriptBlock puede devolver directamente cadenas o bien objetos de tipo System.Management.Automation.CompletionResult, lo que ofrece mucha más flexibilidad, ya que permite definir el texto que se completa, el texto que se muestra en la lista, el tipo de resultado y una descripción o tooltip.

Parámetros del ScriptBlock para completadores de PowerShell

En completadores normales (no nativos), el ScriptBlock debe aceptar cinco parámetros en este orden, independientemente de sus nombres:

  • $commandName: cadena con el nombre del comando para el que se está calculando la finalización.
  • $parameterName: cadena con el nombre del parámetro que se está completando.
  • $wordToComplete: cadena con lo que el usuario ha escrito justo antes de pulsar Tab.
  • $commandAst: objeto CommandAst que representa el árbol de sintaxis abstracta de la línea actual.
  • $fakeBoundParameters: diccionario tipo IDictionary que replica el contenido de $PSBoundParameters en el momento de pulsar Tab.

El parámetro $wordToComplete suele usarse para filtrar valores con operadores como -like, de modo que sólo se sugieran elementos que empiecen por lo que el usuario ya ha tecleado. Mientras tanto, $fakeBoundParameters permite crear completadores dependientes de otros parámetros, como veremos con los ejemplos de frutas y verduras.

Parámetros del ScriptBlock para completadores nativos

Cuando se usa -Native, el ScriptBlock recibe tres parámetros, también por posición:

  • $wordToComplete (posición 0): texto que el usuario ha escrito antes de la tecla Tab.
  • $commandAst (posición 1): árbol de sintaxis del comando completo, no sólo del argumento actual.
  • $cursorPosition (posición 2): índice de la posición del cursor en la línea.

Este formato encaja muy bien con CLIs que ya exponen su propio sistema de autocompletado. El caso típico es dotnet complete, que recibe la línea y la posición del cursor y devuelve sugerencias en texto plano que se transforman después en CompletionResult.

Ejemplos prácticos con Register-ArgumentCompleter

Ejemplo 1: completar dinámicamente zonas horarias

Imagina que quieres facilitar al máximo el uso del cmdlet Set-TimeZone permitiendo que el usuario complete el parámetro Id con la tecla Tab. Puedes crear un ScriptBlock que recupere todas las zonas horarias con Get-TimeZone -ListAvailable, filtre por lo que el usuario ha escrito y envuelva cada valor entre comillas por si hay espacios:

Script de ejemplo 1: $script = {<br> param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)<br> (Get-TimeZone -ListAvailable).Id |<br> Where-Object { $_ -like "$wordToComplete*" } |<br> ForEach-Object { "'$_'" }<br>}<br>Register-ArgumentCompleter -CommandName Set-TimeZone -ParameterName Id -ScriptBlock $script

Con esto, cuando escribas Set-TimeZone -Id y empieces a teclear, al pulsar Tab tendrás una lista de Id de zonas horarias filtradas por lo que llevas escrito. Las comillas simples se añaden para evitar problemas con identificadores que contienen espacios.

Ejemplo 2: sugerencias enriquecidas con CompletionResult

En un segundo escenario, quieres que al usar Stop-Service -Name sólo aparezcan servicios que estén en ejecución, y además deseas mostrar información adicional en la lista de sugerencias cuando el usuario pulsa Ctrl+Espacio.

Script de ejemplo 2 (CompletionResult): $script = {<br> param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)<br> $services = Get-Service | Where-Object {<br> $_.Status -eq 'Running' -and $_.Name -like "$wordToComplete*"<br> }<br> $services | ForEach-Object {<br> New-Object -TypeName System.Management.Automation.CompletionResult -ArgumentList @(<br> $_.Name, # completionText<br> $_.Name, # listItemText<br> 'ParameterValue', # resultType<br> $_.Name # toolTip<br> )<br> }<br>}<br>Register-ArgumentCompleter -CommandName Stop-Service -ParameterName Name -ScriptBlock $script

Atención a estos campos: En cada objeto CompletionResult puedes controlar estos campos clave para una experiencia más pulida:

  • completionText: texto que se inserta realmente en el comando cuando eliges la sugerencia.
  • listItemText: texto mostrado en la lista de propuestas; puede diferir de la cadena insertada.
  • resultType: tipo de resultado (por ejemplo, ParameterValue).
  • toolTip: descripción que aparece al seleccionar el elemento en la lista de completado, especialmente útil con Ctrl+Espacio.
  Cómo solucionar los errores 0x800F0900, 0x800F0922 y 0x80070070 en Windows

Ejemplo 3: completador nativo para la CLI de dotnet

También puedes usar Register-ArgumentCompleter para ampliar el autocompletado de herramientas nativas. Un caso clásico es dotnet, que incluye el subcomando dotnet complete para devolver completados en función de la línea actual.

Script nativo de ejemplo: $scriptblock = {<br> param($wordToComplete, $commandAst, $cursorPosition)<br> dotnet complete --position $cursorPosition $commandAst.ToString() | ForEach-Object {<br> ::new(<br> $_, # completionText<br> $_, # listItemText<br> 'ParameterValue', # resultType<br> $_ # toolTip<br> )<br> }<br>}<br>Register-ArgumentCompleter -Native -CommandName dotnet -ScriptBlock $scriptblock

En este ejemplo, el propio comando dotnet complete es el que realiza el cálculo de las sugerencias. El ScriptBlock simplemente transforma la salida en objetos CompletionResult. De esta forma consigues una integración muy fluida entre PowerShell y una CLI externa, con pestañas que conocen todas las opciones nativas de dotnet.

Otras formas de implementar completadores de argumentos

ValidateSet: valores fijos con validación estricta

El atributo ValidateSet ofrece una manera sencilla de restringir un parámetro o variable a un conjunto cerrado de valores y, a la vez, activar la finalización con Tab sin tener que escribir un ScriptBlock personalizado.

Imagina un parámetro Fruit que sólo acepte «Apple», «Banana» o «Pear». Podrías definirlo así:

Ejemplo de definición: param (<br> <br> <br> ]$Fruit<br>)

Si el usuario introduce un valor que no está en el conjunto, PowerShell lanzará un error de validación. Lo mismo se aplica a variables corrientes, no sólo a parámetros, por ejemplo:

Declaración de variable: <br>$Flavor = 'Strawberry'

Cada vez que se asigne algo a $Flavor, se comprobará que el valor pertenezca al conjunto. Si en algún punto del script haces:

param(<br> <br> $Message<br>)<br>$Message = 'bye'

PowerShell devolverá un error de tipo MetadataError, indicando que la variable dejaría de ser válida si se aplicase ese valor, lo que ayuda a mantener contratos estrictos en tus scripts.

ValidateSet dinámico con clases

Además de conjuntos estáticos, PowerShell permite generar valores de ValidateSet de forma dinámica usando clases que implementan la interfaz System.Management.Automation.IValidateSetValuesGenerator. En este modelo, la clase expone un método GetValidValues() que devuelve las posibles opciones.

Un ejemplo típico es la clase SoundNames, que recorre varias carpetas del sistema en busca de archivos de sonido y devuelve sus nombres base como valores válidos:

Clase de ejemplo (SoundNames): class SoundNames : System.Management.Automation.IValidateSetValuesGenerator {<br> ] GetValidValues() {<br> $SoundPaths = '/System/Library/Sounds/', '/Library/Sounds', '~/Library/Sounds'<br> $SoundNames = foreach ($SoundPath in $SoundPaths) {<br> if (Test-Path $SoundPath) {<br> (Get-ChildItem $SoundPath).BaseName<br> }<br> }<br> return ] $SoundNames<br> }<br>}

Más tarde implementas la clase como un ValidateSet dinámico en un parámetro o variable:

Uso de ValidateSet dinámico: param (<br> )]<br> $Sound<br>)

Así consigues un completado con pestañas que refleja el estado real del sistema de archivos cada vez que se ejecuta el script.

ArgumentCompletions: sugerencias sin validar el valor

El atributo ArgumentCompletions permite añadir una lista de valores sugeridos a un parámetro, pero a diferencia de ValidateSet, no se realiza validación estricta. Eso significa que el usuario puede elegir una de las sugerencias o escribir cualquier otra cosa, incluso aunque no esté en la lista.

Su sintaxis es muy directa. Por ejemplo, puedes definir una función con un parámetro Type que sugiere «Fruits» y «Vegetables», y parámetros Fruit y Vegetable con listas de posibles valores:

Definición de función: function Test-ArgumentCompletions {<br> <br> param (<br> <br> <br> $Type,<br;<br> <br> <br> $Fruit,<br;<br> <br> <br> $Vegetable<br> )<br>}

Este atributo es perfecto cuando quieres orientar al usuario con ejemplos de valores habituales, pero no deseas impedirle introducir otros distintos por requisitos de negocio o por compatibilidad con casos especiales.

ArgumentCompleter: completadores a nivel de atributo de parámetro

El atributo ArgumentCompleter sirve para adjuntar a un parámetro un ScriptBlock con la misma firma que los completadores registrados con Register-ArgumentCompleter, pero sin necesidad de invocar este cmdlet. Es una manera elegante de encapsular lógica compleja de autocompletado directamente en la definición de la función.

Ejemplo básico: Un ejemplo de uso podría ser:

Ejemplo de atributo: function MyArgumentCompleter {<br> param (<br> <br> <br> $ParamName<br> )<br>}

Igual que con Register-ArgumentCompleter, el ScriptBlock debe devolver los valores mediante la canalización, y puede aprovechar tanto $wordToComplete como $fakeBoundParameters o incluso analizar el AST para tener en cuenta el contexto completo del comando.

  Reimage Repair para Windows: uso, riesgos y alternativas

Ejemplo avanzado de ArgumentCompleter dependiente de otro parámetro

Un patrón muy útil consiste en crear completadores que dependan del valor de otros parámetros. Imagina una función con un parámetro Type que puede ser «Fruits» o «Vegetables», y un parámetro Value cuyo conjunto de sugerencias cambia según el tipo elegido.

Implementación del motor: function MyArgumentCompleter {<br> param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)<br><br> $possibleValues = @{<br> Fruits = @('Apple','Orange','Banana')<br> Vegetables = @('Onion','Carrot','Lettuce')<br> }<br><br>> if ($fakeBoundParameters.ContainsKey('Type')) {<br> $possibleValues |<br> Where-Object { $_ -like "$wordToComplete*" }<br> } else {<br> $possibleValues.Values | ForEach-Object { $_ }<br> }<br>}

Consumo del completador: function Test-ArgumentCompleter {<br> <br> param (<br> <br> <br> $Type,<br;<br> <br> <br> $Value<br> )<br>}

En este escenario, si escribes por ejemplo Test-ArgumentCompleter -Type Fruits -Value A y pulsas Tab, gracias al uso de -like y del diccionario $fakeBoundParameters, el completador sólo devolverá valores de frutas que empiecen por «A», es decir, Apple. Se trata de una técnica muy potente para construir interfaces por línea de comandos amigables incluso frente a datos no triviales.

Completadores de argumentos basados en clases (a partir de PowerShell 7.2)

A partir de PowerShell 7.2, se incorporó una funcionalidad que permite definir completadores reutilizables basados en clases, lo que facilita mucho la creación de completadores parametrizables y genéricos.

La idea es derivar de ArgumentCompleterAttribute e implementar la interfaz IArgumentCompleterFactory. La clase derivada puede tener propiedades configurables (por ejemplo, rangos numéricos, profundidad de directorio, fechas, ramas de Git, etc.) que se usan para construir el completador real.

Código de clase (ejemplo): using namespace System.Collections<br>using namespace System.Collections.Generic<br>using namespace System.Management.Automation<br>using namespace System.Management.Automation.Language<br><br>class NumberCompleter : IArgumentCompleter {<br> $From<br> $To<br> $Step<br><br> NumberCompleter( $from, $to, $step) {<br> if ($from -gt $to) {<br> throw ::new("from")<br> }<br> $this.From = $from<br> $this.To = $to<br> $this.Step = $step -lt 1 ? 1 : $step<br> }<br><br> ] CompleteArgument(<br> $CommandName,<br> $parameterName,<br> $wordToComplete,<br> $commandAst,<br;> $fakeBoundParameters<br> ) {<br> $resultList = ]::new()<br> $Local:to = $this.To<br> $Local:step = $this.Step<br><br> for ($i = $this.From; $i -lt $to; $i += $step) {<br> $resultList.Add(::new($i.ToString()))<br> }<br><br> return $resultList<br> }<br>}<br><br>class NumberCompletionsAttribute : ArgumentCompleterAttribute, IArgumentCompleterFactory {<br> $From<br> $To<br> $Step<br><br> NumberCompletionsAttribute( $from, $to, $step) {<br> $this.From = $from<br> $this.To = $to<br> $this.Step = $step<br> }<br><br> Create() {<br> return ::new($this.From, $this.To, $this.Step)<br> }<br>}

Uso del atributo: function Add {<br> param(<br> <br> $X,<br;<br> <br> $Y<br> )<br> $X + $Y<br>}

Al escribir la función y empezar a rellenar los parámetros, Tab te propondrá valores numéricos entre 0 y 100 en saltos de 5, sin que tengas que repetir la lógica de autocompletado en cada función. Esta misma técnica se puede aplicar para rutas de directorios, fechas calculadas, commits de Git de una rama dada y, en general, cualquier escenario donde necesites completadores reutilizables y configurables.

Autocompletado «tipo Linux» en PowerShell con PSReadLine y Register-ArgumentCompleter

Si vienes del mundo GNU/Linux, seguramente estés acostumbrado a la experiencia de autocompletado avanzada que ofrecen shells como zsh, con menús interactivos y sugerencias muy inteligentes. PowerShell, aunque diferente, también permite aproximarse bastante a esa experiencia combinando PSReadLine y Register-ArgumentCompleter.

Por un lado, con Set-PSReadlineKeyHandler puedes cambiar la forma en que se comporta la tecla Tab. Un truco muy cómodo consiste en configurar Tab para que muestre un menú navegable de opciones en lugar de simplemente completar al siguiente elemento:

Comando PSReadLine: Set-PSReadlineKeyHandler -Key Tab -Function MenuComplete

Si insertas esta línea en tu archivo de perfil de PowerShell, por ejemplo editándolo con notepad $profile, conseguirás que cada nueva sesión de PowerShell se inicie con ese comportamiento de Tab, haciendo mucho más agradable explorar las opciones disponibles. Eso sí, puede que necesites ajustar la política de ejecución en tu sistema para que el perfil se ejecute sin restricciones excesivas.

Por otro lado, al combinar esta característica de PSReadLine con completadores registrados mediante Register-ArgumentCompleter (por ejemplo, para winget, dotnet o tus propios módulos), puedes acercarte bastante a la fluidez del autocompletado de Linux. Muchos equipos comparten ScriptBlocks de completadores en sus repositorios, lo que permite que toda la organización tenga un entorno de línea de comandos coherente y muy productivo.