¿Cómo paso un rango de valores en la línea de comando? Pasando una expresión como argumento.
Tengo el siguiente código:
$srv_range = 29..30+40+50..52
$srv_range.GetType()
$NewVMTemplate = New-Object psobject
$NewVMTemplate | Add-Member -MemberType NoteProperty -Name Name -Value $null
$srv_range | % {
$pod= $_
$servers = @()
1..2 | % {
$server = $NewVMTemplate | Select-Object *
$server.Name = "pod" + "{0:D2}" -f $pod + "-srv" + $_
$servers += $server
}
ForEach ( $server in $servers) {
write-host $server.Name
}
}
producción:
PowerCLI C:\ .\eraseme.ps1
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
pod29-srv1
pod29-srv2
pod30-srv1
pod30-srv2
pod40-srv1
pod40-srv2
pod50-srv1
pod50-srv2
pod51-srv1
pod51-srv2
pod52-srv1
pod52-srv2
Quiero ingresar el rango desde CLI, pero obtengo el siguiente resultado con este código
param(
[Parameter(Mandatory=$False)] $srv_range
)
#$srv_range = 29..30+40+50..52
$srv_range.GetType()
$NewVMTemplate = New-Object psobject
$NewVMTemplate | Add-Member -MemberType NoteProperty -Name Name -Value $null
$srv_range | % {
$pod= $_
$servers = @()
1..2 | % {
$server = $NewVMTemplate | Select-Object *
$server.Name = "pod" + "{0:D2}" -f $pod + "-srv" + $_
$servers += $server
}
ForEach ( $server in $servers) {
write-host $server.Name
}
}
PowerCLI C:\ .\eraseme.ps1 29..30+40+50..52
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
pod29..30+40+50..52-srv1
pod29..30+40+50..52-srv2
¿Cómo puedo ingresar el rango desde CLI y obtener el mismo resultado que el primer código?
Su problema es que el argumento 29..30+40+50..52
se trata como una cadena literal en su llamada; no.\eraseme.ps1 29..30+40+50..52
se reconoce como una expresión .
Para forzar el reconocimiento como expresión , incluya el argumento en(...)
el operador de agrupación :
.\eraseme.ps1 (29..30+40+50..52)
Lo mismo se aplica si desea utilizar la salida de (otro) comando como argumento del comando ; p.ej:
# Pass the lines read from file paths.txt as an array to Get-ChildItem
# (Parameter -Path is implied in both commands).
Get-ChildItem (Get-Content paths.txt)
Dos cosas aparte:
• $(...)
, el operador de subexpresión , sólo es necesario en dos casos: (a) para incrustar declaraciones completas , especialmente bucles y condicionales, en otra declaración, y (b) para incrustar una expresión, comando o declaración (s) dentro de "..."
, una cadena expandible (interpolada). Basta (...)
con incrustar un solo comando o expresión en una declaración (e incluso eso no es necesario en el lado derecho de una asignación de variable ). Si bien no es probable, el uso innecesario de $(...)
puede tener efectos secundarios; consulte esta respuesta .
• Puede hacer que su secuencia de comandos sea más robusta declarando su parámetro con un tipo más específico , en cuyo caso un intento de llamarlo con una cadena fallaría de inmediato:
[Parameter(Mandatory=$False)] [int[]] $srv_range
(También se podrían aplicar otras optimizaciones a su secuencia de comandos).
Información de antecedentes opcional
En cuanto a cuando un token sin comillas se trata como una expresión o comando anidado frente a una cadena (ampliable) en modo argumento (ver también: about_Parsing ):
(...)
,$(...)
y@(...)
por sí mismos o al comienzo de un token crean un nuevo contexto de análisis , en el que se pueden usar expresiones o incluso comandos anidados :(...)
es suficiente para una sola expresión o comando.$(...)
(el operador de subexpresión ) puede incluir múltiples expresiones/comandos; también puede@()
(el operador de subexpresión de matriz ) y, además, garantiza que su salida siempre se trate como una matriz .En particular, las siguientes expresiones no se reconocen sin estar encerradas en una de las anteriores:
[...]
(literales de tipo) y acceso a sus miembros, como[Environment]::Version
..
(expresiones de rango) como1..10
Si, al comienzo de un token ,
(...)
,$(...)
o@(...)
van seguidos de caracteres adicionales, el primer carácter adicional se considera el comienzo de un argumento nuevo e independiente .Por el contrario, si están precedidos por un literal sin comillas o una referencia de solo variable ,
$(...)
funciona como dentro"..."
(una cadena expandible),(...)
inicia un nuevo argumento que es una expresión y@(...)
se toma como literal@
al(...)
iniciar nuevamente un nuevo argumento que es una expresión.
Un
@
seguido del nombre de una variable (por ejemplo,@params
) que contiene una colección o tabla hash de valores de parámetros inicia la dispersión de parámetros .@{ ... }
se puede utilizar para pasar un literal de tabla hash (por ejemplo,@{ key = 'value' }
).{ ... }
crea un bloque de script ([scriptblock]
).Por sí mismas o al comienzo de un token , las referencias a variables, incluido el acceso a miembros (acceso a propiedades, llamadas a métodos, indexación), se pueden usar tal cual :
- Expresiones como
$HOME
,$PSVersionTable.PSVersion
,$someArray[0]
y$someString.ToUpper()
se reconocen y se devuelven como su tipo inherente.
- Expresiones como
Sin acceso de miembro, es decir, con una referencia de variable simple como
$HOME
, los caracteres posteriores se consideran (potencialmente) parte del mismo argumento que luego se interpreta como una cadena expandible ; consulte a continuación.Con acceso de miembro, el primero de cualquier carácter adicional se considera el comienzo de un nuevo argumento (por ejemplo,
$foo.Length-more
da como resultado dos argumentos: el valor de$foo.Length
y el literal de cadena-more
).Todo lo demás se trata como una cadena expandible , es decir, similar al contenido de una cadena entre comillas dobles, excepto que los metacaracteres [1] aún necesitan escape y ciertos tokens se interpretan como argumentos múltiples .
Expandible significa que las referencias de variables simples incorporadas (por ejemplo,
$HOME\Desktop
o$env:APPDATA\Test
) se interpolan (se reemplazan con sus valores encadenados).
Tenga en cuenta que esto puede dar como resultado una representación que difiere del formato de salida predeterminado de un valor determinado como se muestra en la consola, por ejemplo (nuevamente, consulte esta respuesta para obtener más información).- Incluya un nombre de variable para
{...}
eliminar la ambigüedad de los caracteres posteriores, si es necesario (por ejemplo,${HOME}
).
- Incluya un nombre de variable para
Para acceder a la propiedad de un valor de variable o usar un índice o llamar a un método o incrustar comandos arbitrarios , debe encerrar la expresión en
$(...)
, por ejemplo,v$($PSVersionTable.PSVersion)
Generalmente, es más seguro encerrar tokens con referencias/expresiones de variables incrustadas
"..."
, porque evita los siguientes casos extremos: *$(...)
al comienzo de un token sin comillas no se interpreta como parte de una cadena expandible , se trata como un argumento separado ( por ejemplo,Write-Output $('ab')c
da como resultado dos argumentos: el resultado de$('ab')
y literalc
). *.
al comienzo de un token seguido inmediatamente de una simple referencia de variable o subexpresión también genera argumentos separados .
(Por ejemplo,.$HOME
da como resultado dos argumentos: literal.
y el valor de$HOME
)Nota: Aunque el resultado de la expansión es una cadena, no necesariamente sigue siendo así: el tipo final está determinado por el tipo del parámetro del comando en cuestión al que está vinculado el valor expandido.
Escapar / citar:
- PowerShell tiene muchos más metacaracteres que
cmd.exe
, y un problema notable es que,
se debe evitar para ser tratado como literal, porque,
es el operador de construcción de matrices de PowerShell.
- PowerShell tiene muchos más metacaracteres que
Para escapar de un solo carácter , antepóngale
`
(comilla invertida) .Para evitar la necesidad de escapar de los metacaracteres individualmente , incluya el valor entre
"..."
(comillas dobles) o'...'
(comillas simples) :Utilice comillas dobles si desea que la cadena se interpola (amplíe) , es decir, si desea poder incrustar referencias variables y subexpresiones.
- Dentro de una cadena entre comillas dobles,
`
escape los siguientes caracteres. para tratarlos como literales:` " $
- Dentro de una cadena entre comillas dobles,
Utilice comillas simples para tratar el valor como un literal .
- Dentro de una cadena entre comillas simples, escape
'
como''
- Dentro de una cadena entre comillas simples, escape
Las comillas simples o dobles suelen ser la forma más sencilla de escapar de los espacios en un valor.
Finalmente, tenga en cuenta que
--%
, el llamado símbolo de parada de análisis (PSv3+), cambia completamente la interpretación de todos los argumentos restantes: diseñado para usarse concmd.exe
líneas de comando heredadas, deja de interpretar el resto de la línea excepto para la expansión de variables de entornocmd.exe
de estilo%...%
. . VerGet-Help about_Parsing
En cuanto al uso de tokens cotizados :
'...'
o"..."
por sí mismos o al comienzo de un token :- Estos se analizan como de costumbre: como una cadena literal (
'...'
) o expandible ("..."
). - Cualquier carácter adicional hace que el primer carácter adicional se considere el comienzo de un argumento nuevo e independiente .
- Estos se analizan como de costumbre: como una cadena literal (
'...'
o"..."
estar precedido por una referencia literal sin comillas o de solo variable :- Se evalúan como de costumbre y el resultado (es decir, sin comillas) se añade a lo que les precede (evaluado).
[1] Los metacaracteres en modo argumento (caracteres con significado sintáctico especial) son:
<space> ' " ` , ; ( ) { } | & < > @ #
.
De estos, < > @ #
sólo son especiales al inicio de una ficha.
Ejemplos
Write-Output 1..10 # STRING: -> '1..10'
Write-Output (1..10) # EXPRESSION: -> @(1, 2, ...)
# Write-Output $(1..10) would work too, but is only necessary if
# the enclosed expression comprises *multiple* statements.
Write-Output [Environment]::Version # STRING: -> '[Environment]::Ticks'
Write-Output ([Environment]::Version) # EXPRESSION: -> a [System.Version] instance.
Write-Output a,b # !! ARRAY @(1, 2), because "," is not escaped.
Write-Output a`,b #`# STRING 'ab'
Write-Output "a,b" # ditto
Write-Output 'a,b' # ditto
Write-Output $HOME\Desktop # EXPANDED string (e.g.) 'C:\Users\jdoe\Desktop'
Write-Output "$HOME\Desktop" # ditto
Write-Output '$HOME\Desktop' # LITERAL string '$HOME\Desktop'
Write-Output dir=$HOME # EXPANDED string (e.g.) 'dir=C:\Users\jdoe\Desktop'
Write-Output $PSVersionTable.PSVersion # a [System.Version] instance
Write-Output "$($PSVersionTable.PSVersion)/more" # a [string]; e.g., '5.1.14393.576/more'
Write-Output "v$($PSVersionTable.PSVersion)" # ditto; e.g., 'v5.1.14393.576'
# !!! These DO NOT WORK as intended.
Write-Output $($PSVersionTable.PSVersion)/more # $(...) at the *start*
Write-Output $PSVersionTable.PSVersion/more # $(...) missing
Write-Output "$PSVersionTable.PSVersion/more" # $(...) missing
Write-Output .$HOME # Specifically, .$ at the beginning is the problem; escaping . works