powershell - receive - Fichas sin comillas en el modo argumento que incluyen referencias y subexpresiones de variables: ¿por qué a veces se dividen en múltiples argumentos?
powershell receive parameters (1)
Nota: desde entonces, se ha publicado un resumen de esta pregunta en el repositorio de PowerShell GitHub , que ha sido reemplazado por este problema más completo .
Los argumentos pasados a un comando en PowerShell se analizan en el modo argumento (a diferencia del modo de expresión , consulte Get-Help about_Parsing
).
Convenientemente, los argumentos de cita (doble) que no contienen espacios en blanco o metacaracteres suelen ser opcionales , incluso cuando estos argumentos involucran referencias de variables (por ejemplo, $HOME/sub
) o subexpresiones (por ejemplo, version=$($PsVersionTable.PsVersion)
.
En su mayor parte, tales argumentos sin comillas se tratan como si fueran cadenas de comillas dobles , y se aplican las reglas habituales de interpolación de cadenas (excepto que los metacaracteres como ,
ejemplo ,
deben escaparse).
He intentado resumir las reglas de análisis de tokens sin comillas en el modo argumento en esta respuesta , pero hay casos curiosos :
Específicamente (a partir de Windows PowerShell v5.1), ¿por qué el token de argumento no citado en cada uno de los siguientes comandos NO se reconoce como una sola cadena expandible y da como resultado que se pasen 2 argumentos (con la variable referencia / subexpresión que mantiene su tipo) ?
$(...)
al inicio de un token:Write-Output $(Get-Date)/today # -> 2 arguments: [datetime] obj. and string ''/today''
Tenga en cuenta que el siguiente trabajo como se esperaba:
-
Write-Output $HOME/sub
- simple var. referencia al inicio -
Write-Output today/$(Get-Date)
- subexpresión no al inicio
-
.$
al inicio de un token:Write-Output .$HOME # -> 2 arguments: string ''.'' and value of $HOME
Tenga en cuenta que el siguiente trabajo como se esperaba:
-
Write-Output /$HOME
- diferentes caracteres iniciales.$
precedente -
Write-Output .-$HOME
- inicial.
no seguido directamente por$
-
Write-Output a.$HOME
-.
no es la charla inicial.
-
Como nota aparte: a partir de PowerShell Core v6.0.0-alpha.15, a =
siguiendo una var simple. La referencia al inicio de un token también parece dividir el token en 2 argumentos, lo que no sucede en Windows PowerShell v5.1; por ejemplo, Write-Output $HOME=dir
.
Nota:
Principalmente estoy buscando una justificación de diseño para el comportamiento descrito o, según sea el caso, la confirmación de que es un error. Si no es un error, quiero algo que me ayude a conceptualizar el comportamiento, para poder recordarlo y evitar sus trampas.
Todos estos casos de borde se pueden evitar con una doble cita explícita, que, dado el comportamiento no obvio de arriba, puede ser la opción más segura de usar de forma rutinaria.
Lectura opcional: El estado de la documentación y reflexiones de diseño.
A partir de este escrito, la página v5.1 Get-Help about_Parsing
:
describe de manera incompleta las reglas
usa términos que no están ni definidos en el tema ni generalmente se usan comúnmente en el mundo de PowerShell ("cadena expandible", "expresión de valor", aunque uno puede adivinar su significado)
Desde la página enlazada (énfasis añadido):
En el modo argumento, cada valor se trata como una cadena expandible a menos que comience con uno de los siguientes caracteres especiales : signo de dólar (
$
), signo (@
), comillas simples (''
), comillas dobles ("
) o un paréntesis de apertura ((
).Si está precedido por uno de estos caracteres, el valor se trata como una expresión de valor .
Como nota al margen: un token que comienza con "
es, por supuesto, por definición, también una cadena ampliable (cadena de interpolación).
Curiosamente, el tema de ayuda conceptual sobre las Get-Help about_Quoting_Rules
, Get-Help about_Quoting_Rules
, logra evitar tanto los términos "expandir" como "interpolar".
Observe cómo el pasaje no indica qué sucede cuando los caracteres (no meta) siguen directamente un token que comienza con estos caracteres especiales, notablemente $
.
Sin embargo, la página contiene un ejemplo que muestra que un token que comienza con una referencia variable también se interpreta como una cadena expandible :
- Con
$a
contiene4
,Write-Output $a/H
evalúa como (argumento de cadena única)4/H
Tenga en cuenta que el pasaje implica que las referencias / subexpresiones variables en el interior de un token sin comillas (que no comienza con un carácter especial) se expanden como si estuvieran dentro de una cadena de comillas dobles ("tratadas como una cadena expandible").
Si estos funcionan:
$a = 4
Write-Output $a/H # -> ''4/H''
Write-Output H/$a # -> ''H/4''
Write-Output H/$(2 + 2) # -> ''H/4''
¿por qué no debería Write-Output $(2 + 2)/H
expandir a ''4/H''
también (en lugar de ser tratado como 2 argumentos?
¿Por qué una subexpresión al inicio se trata de manera diferente a una referencia de variable?
Tales distinciones sutiles son difíciles de recordar, especialmente en ausencia de una justificación.
Una regla que tendría más sentido para mí es tratar incondicionalmente un token que comienza con $
y tiene caracteres adicionales que siguen la referencia / subexpresión de la variable como una cadena expandible también.
(Por el contrario, tiene sentido que una referencia / subexpresión de variable independiente conserve su tipo , como lo hace ahora).
Tenga en cuenta que el caso de un token que comienza con .$
divide en 2 argumentos no se trata en absoluto en el tema de ayuda.
Aún más lectura opcional: siguiendo un token que comienza con uno de los otros caracteres especiales con caracteres adicionales.
Entre los otros caracteres especiales de inicio de tokens, los siguientes tratan incondicionalmente a los caracteres que siguen el final de la construcción como un argumento separado (lo que tiene sentido):
( '' "
Write-Output (2 + 2)/H # -> 2 arguments: 4 and ''/H''
Write-Output "2 + $a"/H # -> 2 arguments: ''2 + 4'' and ''/H'', assuming $a equals 4
Write-Output ''2 + 2''/H # -> 2 arguments: ''2 + 2'' and ''/H''
Como nota al margen: Esto muestra que la concatenación de cadenas de estilo bash
(colocar cualquier combinación de tokens citados y no citados uno al lado del otro) generalmente no se admite en PowerShell; solo funciona si la primera subcadena / referencia de variable pasa a estar sin comillas . Por ejemplo, Write-Output H/''2 + 2''
, a diferencia del ejemplo anterior de subcadenas invertidas, produce un solo argumento.
La excepción es @
: mientras que @
tiene un significado especial (consulte Get-Help about_Splatting
) cuando está seguido solo por un nombre de variable sintácticamente válida (p. Ej., @parms
), todo lo demás hace que el token sea tratado como una cadena ampliable nuevamente:
Write-Output @parms # splatting (results in no arguments if $parms is undefined)
Write-Output @parms$a # *expandable string*: ''@parms4'', if $a equals 4
Creo que lo que estás golpeando aquí es más el tipo de "insinuación" que cualquier otra cosa.
Estás usando Write-Output que especifica en su Sinopsis que
Envía los objetos especificados al siguiente comando en la canalización.
Este comando está diseñado para tomar en una matriz. Cuando golpea el primer elemento como una cadena como hoy / la trata como una cadena. Cuando el primer elemento termina siendo el resultado de una llamada de función, puede ser o no una cadena, por lo que inicia una matriz.
Es revelador que si ejecuta el mismo comando para Write-Host (que está diseñado para tomar una cadena de salida), funciona como se espera:
Write-Host $(Get-Date)/today
Salidas
7/25/2018 1:30:43 PM / hoy
Así que creo que los casos de borde con los que se está enfrentando son menos sobre el análisis, y más sobre la tipificación que utiliza powershell (y trata de ocultar).