Para los cmdlets de PowerShell, ¿puedo pasar siempre un bloque de script a un parámetro de cadena?
parameters scriptblock (3)
Estoy mirando el documento de
Rename-Item
y hay un ejemplo como este.
PS C:/>Get-ChildItem *.txt | Rename-Item -NewName { $_.name -Replace ''/.txt'',''.log'' }
Este ejemplo muestra cómo usar el operador Reemplazar para cambiar el nombre de varios archivos, aunque el parámetro NewName no acepte caracteres comodín.
Este comando cambia el nombre de todos los archivos .txt en el directorio actual a .log.
El comando usa el cmdlet Get-ChildItem para obtener todos los archivos en la carpeta actual que tienen una extensión de nombre de archivo .txt. Luego, utiliza el operador de canalización (|) para enviar esos archivos a Rename-Item.
El valor de NewName es un bloque de script que se ejecuta antes de que el valor se envíe al parámetro NewName.
Tenga en cuenta la última oración:
El valor de NewName es un bloque de script que se ejecuta antes de que el valor se envíe al parámetro NewName .
En realidad,
NewName
es una cadena:
[-NewName] <String>
Entonces, ¿eso significa que siempre puedo usar un bloque de script cuando el tipo de parámetro requerido es una cadena?
Entonces, ¿eso significa que siempre puedo usar un bloque de script cuando el tipo de parámetro requerido es una cadena? : NO
Aquí la técnica se llama Delay Binding , que es muy útil en este escenario.
¿Qué sucede cuando retrasas el enlace?
PowerShell ParameteBinder comprenderá el uso del enlace de retraso y ejecutará primero el ScriptBlock y luego la salida se convertirá al tipo esperado del parámetro respectivo, aquí es una cadena.
A continuación se muestra un ejemplo.
#Working one
''Path''|Join-Path -Path {$_} -ChildPath ''File''
#Not working one
Join-Path -Path {''path''} -ChildPath ''File''
Join-Path : Cannot evaluate parameter ''Path'' because its argument is specified as a script block and there is no input. A script block cannot be evaluated without input.
Para saber más sobre ParameterBinding, puede hacer un
Trace-Command
como se muestra a continuación.
Trace-Command ParameterBinding -Expression {''Path''|Join-Path -Path {$_} -ChildPath ''File''} -PSHost
Con Delay Binding, el parámetro puede recibir un valor de la canalización utilizando un bloque de script en lugar del tipo de datos real del parámetro.
En el bloque de script, $ _ representa el valor canalizado.
Solo está disponible cuando hay entrada entrando en la tubería.
Los argumentos de bloque de script de retardo-enlace son una característica implícita que:
-
solo funciona con parámetros diseñados para recibir entradas de canalización ,
-
de cualquier tipo, excepto los siguientes , en cuyo caso se produce un enlace de parámetro regular [1] :
-
[scriptblock]
-
[object]
([psobject]
, sin embargo, funciona, y por lo tanto[pscustomobject]
también) -
(sin tipo especificado), que es efectivamente el mismo que
[object]
-
-
si dichos parámetros aceptan la entrada de canalización por valor (
ValueFromPipelineBy
) o por nombre de propiedad (ValueFromPipelineByPropertyName
), es irrelevante.
-
-
permite transformaciones por objeto de entrada a través de un bloque de script pasado en lugar de un argumento apropiado para el tipo ; el bloque de secuencia de comandos se evalúa para cada objeto de canalización, al que se puede acceder dentro del bloque de secuencia de comandos como
$_
, como de costumbre, y la salida del bloque de secuencia de comandos, que se supone que es apropiada para el tipo de parámetro, se usa como argumento.-
Dado que tales bloques de script ad-hoc, por definición, no coinciden con el tipo de parámetro al que se dirige, siempre debe usar el nombre del parámetro explícitamente al pasarlos.
-
Los bloques de script de enlace diferido brindan acceso incondicional a los objetos de entrada de canalización, incluso si el parámetro normalmente no estaría vinculado a un objeto de canalización dado, si se define como
ValueFromPipelineByPropertyName
y el objeto carece de una propiedad con ese nombre.-
Esto habilita técnicas como la siguiente llamada a
Rename-Item
, donde la entrada de canalización deGet-Item
está, como de costumbre, vinculada al parámetro-LiteralPath
, pero pasando un bloque de script a-NewName
, que normalmente solo se uniría a objetos de entrada con una propiedad.NewName
: permite el acceso al mismo objeto de canalización y, por lo tanto, deriva el nombre del archivo de destino del nombre del archivo de entrada:-
Get-Item file | Rename-Item -NewName { $_.Name + ''1'' } # renames ''file'' to ''file1''
Get-Item file | Rename-Item -NewName { $_.Name + ''1'' } # renames ''file'' to ''file1''
; La entrada se une tanto a-LiteralPath
(implícitamente) como al bloque de script-NewName
.
-
-
Esto habilita técnicas como la siguiente llamada a
-
Nota: a diferencia de los bloques de script pasados a
ForEach-Object
oWhere-Object
, por ejemplo, los bloques de script de retraso-enlace se ejecutan en un ámbito de variable secundario [2] , lo que significa que no puede modificar directamente las variables de la persona que llama , como incrementar un contador a través de objetos de entrada
Como solución alternativa, use una variable de tipo[ref]
declarada en el alcance de la persona que llama y acceda a su propiedad.Value
dentro del bloque de script; consulte esta respuesta para ver un ejemplo.
-
[1] Condiciones de error:
-
Si por error intenta pasar un bloque de secuencia de comandos a un parámetro que no está vinculado a la canalización o que es
[scriptblock]
- o[object]
(sin tipo) , se produce la vinculación de parámetros regular :-
El bloque de script se pasa
una vez
, antes de que comience el proceso de entrada de canalización, si lo hay.
Es decir, el bloque de script se pasa como un valor (posiblemente convertido) y no se realiza ninguna evaluación .-
Para los parámetros de tipo
[object]
o[scriptblock]
/ un tipo de delegado comoSystem.Func
que es convertible en un bloque de script, el bloque de script se unirá tal cual . -
En el caso de un parámetro de tipo
[string]
(sin vinculación de canalización), el contenido literal del bloque de script se pasa como el valor de la cadena. - Para todos los demás tipos, el enlace de parámetros, y por lo tanto el comando como un todo, simplemente fallará , ya que la conversión desde un bloque de script no es posible.
-
Para los parámetros de tipo
-
El bloque de script se pasa
una vez
, antes de que comience el proceso de entrada de canalización, si lo hay.
-
Si no proporciona la entrada de canalización mientras pasa un bloque de script de enlace de retraso a un parámetro de enlace de canalización que sí los admite, obtendrá el siguiente error :
-
Cannot evaluate parameter ''<name>'' because its argument is specified as a script block and there is no input. A script block cannot be evaluated without input.
-
[2] Esta discrepancia se está discutiendo en este número de GitHub .