Cómo pasar argumentos de la línea de comandos a un archivo ps1 de PowerShell

90

Durante años, he usado el cmd/DOS/Windowsshell y he pasado argumentos de línea de comandos a archivos por lotes. Por ejemplo, tengo un archivo, zuzu.baty en ella, el acceso I %1, %2, etc. Ahora, quiero hacer lo mismo cuando llamo a un PowerShellguión when I am in a Cmd.exe shell. Tengo un script xuxu.ps1(y agregué PS1 a mi variable PATHEXT y archivos PS1 asociados con PowerShell). Pero no importa lo que haga, parece que no puedo obtener nada de la $argsvariable. Siempre tiene una longitud de 0.

Si estoy en un PowerShellcaparazón, en lugar de cmd.exe, funciona (por supuesto). Pero todavía no me siento lo suficientemente cómodo para vivir en el entorno de PowerShell a tiempo completo. No quiero escribir powershell.exe -command xuxu.ps1 p1 p2 p3 p4. Quiero escribir xuxu p1 p2 p3 p4.

¿Es posible? y si lo es, cómo?

La muestra que no puedo hacer para trabajar es trivial, foo.ps1:

Write-Host "Num Args:" $args.Length;
foreach ($arg in $args) {
    Write-Host "Arg: $arg";
}

Los resultados siempre son así:

C:\temp> foo
Num Args: 0
C:\temp> foo a b c d
Num Args: 0
c:\temp>
Daniel 'Dang' Griffith
fuente

Respuestas:

34

Este artículo ayuda. En particular, esta sección:

-Archivo

Ejecuta el script especificado en el ámbito local ("dot-source"), de modo que las funciones y variables que crea el script estén disponibles en la sesión actual. Introduzca la ruta del archivo de secuencia de comandos y los parámetros. El archivo debe ser el último parámetro del comando, porque todos los caracteres escritos después del nombre del parámetro Archivo se interpretan como la ruta del archivo de secuencia de comandos seguida de los parámetros de la secuencia de comandos.

es decir

powershell.exe -File "C:\myfile.ps1" arg1 arg2 arg3

significa ejecutar el archivo myfile.ps1 y arg1 arg2 y arg3 son los parámetros para el script de PowerShell.

Arj
fuente
1
Esto todavía no ayuda con lo que quiere el operador ("Quiero escribir xuxu p1 p2 p3 p4").
thdoan
19

Después de examinar la documentación de PowerShell, descubrí información útil sobre este problema. No puede usar el $argssi usó el param(...)al principio de su archivo; en su lugar, necesitarás usar$PSBoundParameters . Copié / pegué su código en un script de PowerShell y funcionó como era de esperar en la versión 2 de PowerShell (no estoy seguro de en qué versión estaba cuando se encontró con este problema).

Si está usando $PSBoundParameters(y esto SOLO funciona si está usando param(...)al principio de su script), entonces no es una matriz, es una tabla hash, por lo que deberá hacer referencia a ella usando el par clave / valor.

param($p1, $p2, $p3, $p4)
$Script:args=""
write-host "Num Args: " $PSBoundParameters.Keys.Count
foreach ($key in $PSBoundParameters.keys) {
    $Script:args+= "`$$key=" + $PSBoundParameters["$key"] + "  "
}
write-host $Script:args

Y cuando se llama con ...

PS> ./foo.ps1 a b c d

El resultado es...

Num Args:  4
$p1=a  $p2=b  $p3=c  $p4=d
Randall Borck
fuente
Esto no tiene en cuenta que OP inicie su línea de comando con powershell.exe o pwsh. El comportamiento cambia cuando OP hace eso.
Eric Hansen
1
@EricHansen No sé a qué te refieres, obtengo el mismo resultado de cualquier manera: `Powershell> powershell.exe. \ ParamTest.ps1 val1 val2 val3 val4 Num Args: 4 $ p1 = val1 $ p2 = val2 $ p3 = val3 $ p4 = val4 `
Randall Borck
@RandallBrock El comportamiento de los argumentos cambia para mí. Si estoy en CMD / batch, y hago algo como pwsh .\ParamTest.ps1 -arg1 val1 -listOfArgs val2 val3 val4, realmente no le gusta eso. Por otro lado, si estoy en PowerShell y lo hago .\ParamTest.ps1 -arg1 val1 -listOfArgs val2 val3 val4, eso funciona como esperaba. Escuché que así es como debe funcionar debido a "razones de seguridad".
Eric Hansen
@EricHansen Me pregunto si es una versión. Para mí, pwsh lanza 6.2.0, pero powershell.exe lanza 5.1.17134.858, y ambos producen los mismos resultados enumerados: Powershell>pwsh .\ParamTest.ps1 val1 val2 val3 val4rendimientos:Num Args: 4 $p1=val1 $p2=val2 $p3=val3 $p4=val4
Randall Borck
1
@Timo No sé qué estás haciendo exactamente, pero parames una construcción de lenguaje, sin embargo, debe ser lo primero en el archivo. ¿Declaraste una variable o algo más antes? Más información aquí: docs.microsoft.com/en-us/powershell/module/…
Randall Borck
18

Bien, primero esto está rompiendo una característica de seguridad básica en PowerShell. Con ese entendimiento, así es como puede hacerlo:

  1. Abra un explorador de Windows ventana del
  2. Herramientas de menú -> Opciones de carpeta -> pestaña Tipos de archivo
  3. Busque el tipo de archivo PS1 y haga clic en el botón avanzado
  4. Haga clic en el botón Nuevo
  5. Para poner en acción: Abierto
  6. Para la aplicación, coloque: "C: \ WINNT \ system32 \ WindowsPowerShell \ v1.0 \ powershell.exe" "-file" "% 1"% *

Es posible que también desee incluir un -NoProfileargumento en función de lo que haga su perfil.

EBGreen
fuente
4
Creo que la clave está en el paso 6, donde pasa los parámetros a powershell.exe. Daniel dice que ha asociado archivos PS1 con PowerShell, pero eso no pasa argumentos sin las especificaciones% 1% * adicionales. También tenga en cuenta que el parámetro -File no está disponible en V1. Es nuevo en V2.
Keith Hill
Buen truco con respecto al parámetro -file, lo había olvidado.
EBGreen
Tendré que instalar V2 antes de poder probar tu sugerencia. Gracias. Cuando dice que esto está rompiendo una característica de seguridad básica, ¿a qué se refiere "esto"? ¿Llamar a un script de PowerShell desde Cmd.exe como si fuera un archivo .com / .bat / .exe? ¿Pasando parámetros al script?
Daniel 'Dang' Griffith
1
Lo siento, debería haber sido más claro. La llamada al script sin llamar explícitamente a powershell.exe. No estoy diciendo que sea una característica de seguridad significativa para usted personalmente y es la seguridad a través de la oscuridad de la que no siempre soy fanático.
EBGreen
6
Para agregar al comentario de EBGreen, el problema de seguridad básico que PowerShell intenta evitar es que la gente haga doble clic en los archivos PS1 adjuntos al correo electrónico y ejecute el script. Es por eso que los archivos PS1 solo están asociados con un editor de forma predeterminada. Microsoft realmente no quiere una versión PowerShell del virus ILoveYou, por ejemplo, "LOVE-LETTER-FOR-YOU.TXT.ps1"
Keith Hill
11

Puede declarar sus parámetros en el archivo, como param:

[string]$para1
[string]$param2

Y luego llame al archivo PowerShell así .\temp.ps1 para1 para2....para10, etc.

VB
fuente
6

Tal vez pueda envolver la invocación de PowerShell en un .batarchivo como este:

rem ps.bat
@echo off
powershell.exe -command "%*"

Si luego colocó este archivo en una carpeta en su PATH, podría llamar scripts de PowerShell como este:

ps foo 1 2 3

Sin embargo, las citas pueden ser un poco complicadas:

ps write-host """hello from cmd!""" -foregroundcolor green
Guillermooo
fuente
+1 para mostrar las comillas triples. eso me tuvo atrapado por un tiempo.
DeanOC
1

Es posible que no obtenga "xuxu p1 p2 p3 p4" como parece. Pero cuando está en PowerShell y establece

PS > set-executionpolicy Unrestricted -scope currentuser

Puede ejecutar esos scripts como este:

./xuxu p1 p2 p3 p4

o

.\xuxu p1 p2 p3 p4

o

./xuxu.ps1 p1 p2 p3 p4

Espero que eso te haga sentir un poco más cómodo con PowerShell.

Atiq Rahman
fuente
0

si desea invocar scripts de ps1 desde cmd y pasar argumentos sin invocar el script como

powershell.exe script.ps1 -c test
script -c test ( wont work )

puedes hacer lo siguiente

setx PATHEXT "%PATHEXT%;.PS1;" /m
assoc .ps1=Microsoft.PowerShellScript.1
ftype Microsoft.PowerShellScript.1=powershell.exe "%1" %*

Esto es asumiendo que powershell.exe está en su camino

https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/ftype

ta32
fuente
Vale la pena señalar que esto requiere ejecutar el indicador de cmd como administrador. Además, estoy luchando para que funcione en Windows 10 versión 1903 (18362.778): todos los comandos se ejecutan correctamente pero los argumentos aún no se transmiten. Creo que envolver con un archivo .bat es la solución más portátil.
David Airapetyan