Como passar argumentos de linha de comando para um arquivo PowerShell ps1

90

Durante anos, usei o cmd/DOS/Windowsshell e passei argumentos de linha de comando para arquivos em lote. Por exemplo, eu tenho um arquivo, zuzu.bate nele, o acesso I %1, %2etc. Agora, eu quero fazer o mesmo quando eu chamar um PowerShellroteiro when I am in a Cmd.exe shell. Eu tenho um script xuxu.ps1(e adicionei PS1 à minha variável PATHEXT e os arquivos PS1 associados ao PowerShell). Mas não importa o que eu faça, parece que não consigo obter nada da $argsvariável. Sempre tem comprimento 0.

Se eu estiver em um PowerShellshell, em vez de cmd.exe, ele funciona (é claro). Mas ainda não estou confortável o suficiente para viver no ambiente do PowerShell em tempo integral. Eu não quero digitar powershell.exe -command xuxu.ps1 p1 p2 p3 p4. Eu quero digitar xuxu p1 p2 p3 p4.

Isso é possível e, em caso afirmativo, como?

O exemplo que não consigo fazer é trivial, foo.ps1:

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

Os resultados são sempre assim:

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

Respostas:

34

Este artigo ajuda. Em particular, esta seção:

-Arquivo

Executa o script especificado no escopo local ("dot-sourced"), de modo que as funções e variáveis ​​que o script cria estejam disponíveis na sessão atual. Insira o caminho do arquivo de script e quaisquer parâmetros. Arquivo deve ser o último parâmetro no comando, porque todos os caracteres digitados após o nome do parâmetro Arquivo são interpretados como o caminho do arquivo de script seguido pelos parâmetros de script.

ie

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

significa executar o arquivo myfile.ps1 e arg1 arg2 & arg3 são os parâmetros para o script do PowerShell.

Arj
fonte
1
Isso ainda não ajuda com o que o operador deseja ("Eu quero digitar xuxu p1 p2 p3 p4").
thdoan
19

Depois de vasculhar a documentação do PowerShell, descobri algumas informações úteis sobre esse problema. Você não pode usar o $argsse usou param(...)no início do arquivo; em vez disso, você precisará usar $PSBoundParameters. Eu copiei / colei seu código em um script do PowerShell e funcionou como você esperava no PowerShell versão 2 (não tenho certeza de qual versão você estava usando quando encontrou este problema).

Se você estiver usando $PSBoundParameters(e isso SÓ funciona se estiver usando param(...)no início do seu script), então não é um array, é uma tabela hash, portanto, você precisará referenciá-lo usando o par chave / 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

E quando chamado com ...

PS> ./foo.ps1 a b c d

O resultado é...

Num Args:  4
$p1=a  $p2=b  $p3=c  $p4=d
Randall Borck
fonte
Isso não leva em conta que OP inicia sua linha de comando com powershell.exe ou pwsh. O comportamento muda quando o OP faz isso.
Eric Hansen
1
@EricHansen Não sei o que quer dizer, obtenho o mesmo resultado de qualquer maneira: `Powershell> powershell.exe. \ ParamTest.ps1 val1 val2 val3 val4 Num Args: 4 $ p1 = val1 $ p2 = val2 $ p3 = val3 $ p4 = val4 `
Randall Borck
@RandallBrock O comportamento dos argumentos muda para mim. Se estou no CMD / batch e faço algo assim pwsh .\ParamTest.ps1 -arg1 val1 -listOfArgs val2 val3 val4, realmente não gosto disso. Por outro lado, se estou no PowerShell e faço .\ParamTest.ps1 -arg1 val1 -listOfArgs val2 val3 val4isso, funciona como eu esperava. Ouvi dizer que é assim que ele deve funcionar por "motivos de segurança".
Eric Hansen
@EricHansen Eu me pergunto se é uma coisa de versão. Para mim, pwsh inicia 6.2.0, mas powershell.exe inicia 5.1.17134.858, os quais produzem os mesmos resultados listados: Powershell>pwsh .\ParamTest.ps1 val1 val2 val3 val4rendimentos:Num Args: 4 $p1=val1 $p2=val2 $p3=val3 $p4=val4
Randall Borck
1
@Timo Não sei o que você está fazendo exatamente, mas paramé uma construção de linguagem, porém deve ser a primeira coisa no arquivo. Você declarou uma variável ou algo mais antes dela? Mais informações aqui: docs.microsoft.com/en-us/powershell/module/…
Randall Borck
18

OK, então primeiro isso está quebrando um recurso básico de segurança no PowerShell. Com esse entendimento, aqui está como você pode fazer isso:

  1. Abra uma janela do Windows Explorer
  2. Ferramentas de menu -> Opções de pasta -> guia Tipos de arquivo
  3. Encontre o tipo de arquivo PS1 e clique no botão avançado
  4. Clique no botão Novo
  5. Para ação coloque: aberto
  6. Para o Aplicativo, coloque: "C: \ WINNT \ system32 \ WindowsPowerShell \ v1.0 \ powershell.exe" "-file" "% 1"% *

Você também pode querer apresentar um -NoProfileargumento, dependendo do que o seu perfil faz.

EBGreen
fonte
4
Acho que a chave está na sua etapa 6, onde você passa os parâmetros para o powershell.exe. Daniel diz que associou arquivos PS1 ao PowerShell, mas isso não passa argumentos sem as especificações extras de% 1% *. Observe também que o parâmetro -File não está disponível em V1. É novo no V2.
Keith Hill de
Boa captura em relação ao parâmetro -file. Eu tinha esquecido disso.
EBGreen
Terei que instalar o V2 ​​antes de tentar sua sugestão. Obrigado. Quando você diz que isso está quebrando um recurso básico de segurança, o que quer dizer "isso"? Chamando um script do PowerShell do Cmd.exe como se fosse um arquivo .com / .bat / .exe? Passando parâmetros para o script?
Daniel 'Dang' Griffith
1
Desculpe, eu deveria ter sido mais claro. A chamada do script sem chamar explicitamente o powershell.exe. Não estou dizendo que é um recurso de segurança significativo para você pessoalmente e é a segurança através da obscuridade da qual nem sempre sou um fã.
EBGreen
6
Para adicionar ao comentário do EBGreen, o problema básico de segurança que o PowerShell tenta evitar é clicar duas vezes nos arquivos PS1 anexados ao e-mail e executar o script. É por isso que os arquivos PS1 são associados apenas a um editor por padrão. A Microsoft realmente não quer uma versão PowerShell do vírus ILoveYou, por exemplo, "LOVE-LETTER-FOR-YOU.TXT.ps1"
Keith Hill
12

Você pode declarar seus parâmetros no arquivo, como param:

[string]$para1
[string]$param2

Em seguida, chame o arquivo PowerShell assim .\temp.ps1 para1 para2....para10, etc.

VB
fonte
6

Talvez você possa agrupar a invocação do PowerShell em um .batarquivo assim:

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

Se, em seguida, você colocou esse arquivo em uma pasta em seu PATH, poderá chamar scripts do PowerShell como este:

ps foo 1 2 3

Citar pode ser um pouco confuso:

ps write-host """hello from cmd!""" -foregroundcolor green
Guilhermeoo
fonte
1 para mostrar as aspas triplas. isso me deixou preso por um tempo.
DeanOC
1

Você pode não obter "xuxu p1 p2 p3 p4" como parece. Mas quando você está no PowerShell e define

PS > set-executionpolicy Unrestricted -scope currentuser

Você pode executar esses scripts como este:

./xuxu p1 p2 p3 p4

ou

.\xuxu p1 p2 p3 p4

ou

./xuxu.ps1 p1 p2 p3 p4

Espero que isso o deixe um pouco mais confortável com o PowerShell.

Atiq Rahman
fonte
0

se você deseja invocar scripts ps1 de cmd e passar argumentos sem invocar o script como

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

você pode fazer o seguinte

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

Isso pressupõe que powershell.exe esteja em seu caminho

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

ta32
fonte
É importante notar que isso requer a execução do prompt cmd como Administrador. Além disso, estou lutando para realmente fazê-lo funcionar no Windows 10 versão 1903 (18362.778) - todos os comandos são executados com êxito, mas os argumentos ainda não são transmitidos. Acho que empacotar com um arquivo .bat é a solução mais portátil.
David Airapetyan