¿Cómo paso un rango de valores en la línea de comando? Pasando una expresión como argumento.

Resuelto user3192279 asked hace 7 años • 1 respuestas

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?

user3192279 avatar Dec 21 '16 09:12 user3192279
Aceptado

Su problema es que el argumento 29..30+40+50..52se 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.
  • 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-moreda como resultado dos argumentos: el valor de $foo.Lengthy 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\Desktopo $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}).
    • 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')cda como resultado dos argumentos: el resultado de $('ab')y literal c). * .al comienzo de un token seguido inmediatamente de una simple referencia de variable o subexpresión también genera argumentos separados .
      (Por ejemplo, .$HOMEda 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.
      • 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: ` " $
        • Utilice comillas simples para tratar el valor como un literal .

          • Dentro de una cadena entre comillas simples, escape ' como''
      • 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 con cmd.exelíneas de comando heredadas, deja de interpretar el resto de la línea excepto para la expansión de variables de entorno cmd.exede 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 .
  • '...'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
mklement0 avatar Dec 21 '2016 02:12 mklement0