No PowerShell, como defino uma função em um arquivo e a chamo na linha de comando do PowerShell?

243

Eu tenho um arquivo .ps1 no qual desejo definir funções personalizadas.

Imagine que o arquivo se chama MyFunctions.ps1 e o conteúdo é o seguinte:

Write-Host "Installing functions"
function A1
{
    Write-Host "A1 is running!"
}
Write-Host "Done"

Para executar esse script e, teoricamente, registrar a função A1, eu navego para a pasta na qual o arquivo .ps1 reside e execute o arquivo:

.\MyFunctions.ps1

Isso gera:

Installing functions
Done

No entanto, quando tento chamar A1, simplesmente recebo o erro informando que não há comando / função com esse nome:

The term 'A1' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling
 of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:3
+ A1 <<<<
    + CategoryInfo          : ObjectNotFound: (A1:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

Devo interpretar mal alguns conceitos do PowerShell. Não consigo definir funções nos arquivos de script?

Observe que eu já defini minha política de execução como 'RemoteSigned'. E sei executar arquivos .ps1 usando um ponto na frente do nome do arquivo:. \ MyFile.ps1

willem
fonte
Bom link para carregar funções na inicialização do PS: sandfeld.net/powershell-load-your-functions-at-startup #
Andrew

Respostas:

262

Tente isso na linha de comando do PowerShell:

. .\MyFunctions.ps1
A1

O operador de ponto é usado para incluir scripts.

rsc
fonte
11
Bem, significa "executar isso no contexto atual, em vez de um contexto filho".
JasonMArcher 17/05
15
Significa fonte do conteúdo deste arquivo. O mesmo que no bash . ss64.com/bash/period.html
informe
2
Embora não pareça funcionar muito bem (pelo menos no ISE), a menos que você execute. \ MyFunctions.ps1 primeiro para disponibilizá-lo. Não tenho certeza sobre como executar estritamente a partir do powershell.exe.
11133 Mike Cheel
1
Eu pensei que era contra-intuitivo que a fonte de pontos usasse o caminho relativo ao pwd em vez do script, por isso instaria as pessoas a procurar a resposta de JoeG e usar os módulos.
Spork
5
@Spork . "$PSScriptRoot\MyFunctions.ps1". Disponível a partir da v3, antes disso, consulte stackoverflow.com/questions/3667238/… . É MUITO comum.
yzorg 5/02
233

O que você está falando é chamado dot sourcing . E é mau. Mas não se preocupe, existe uma maneira melhor e mais fácil de fazer o que você quer com os módulos (parece muito mais assustador do que é). O principal benefício do uso de módulos é que você pode descarregá-los do shell, se necessário, e evita que as variáveis ​​nas funções entrem no shell (depois que você pontuar um arquivo de função, tente chamar uma das variáveis ​​de um função no shell, e você verá o que quero dizer).

Primeiro, renomeie o arquivo .ps1 que possui todas as suas funções para MyFunctions.psm1 (você acabou de criar um módulo!). Agora, para um módulo carregar corretamente, você precisa fazer algumas coisas específicas com o arquivo. Primeiro, para o Import-Module ver o módulo (você usa esse cmdlet para carregar o módulo no shell), ele deve estar em um local específico. O caminho padrão para a pasta de módulos é $ home \ Documents \ WindowsPowerShell \ Modules.

Nessa pasta, crie uma pasta chamada MyFunctions e coloque o arquivo MyFunctions.psm1 nela (o arquivo do módulo deve residir em uma pasta com exatamente o mesmo nome que o arquivo PSM1).

Feito isso, abra o PowerShell e execute este comando:

Get-Module -listavailable

Se você vir um chamado MyFunctions, fez tudo certo e seu módulo está pronto para ser carregado (isso é apenas para garantir que ele esteja configurado corretamente, você só precisa fazer isso uma vez).

Para usar o módulo, digite o seguinte no shell (ou coloque esta linha no seu perfil $ ou coloque como a primeira linha em um script):

Import-Module MyFunctions

Agora você pode executar suas funções. O legal disso é que, quando você tiver 10 a 15 funções, vai esquecer o nome de um casal. Se você os tiver em um módulo, poderá executar o seguinte comando para obter uma lista de todas as funções em seu módulo:

Get-Command -module MyFunctions

É bem legal, e o pequeno esforço que é necessário para montar na frente vale MUITO.

JoeG
fonte
6
E se suas funções forem pertinentes apenas àquele aplicativo do PowerShell? Quero dizer, se você instalar um pacote de PS1 para fazer um trabalho em algum lugar, pode não querer todas as funções do seu perfil, certo?
Ian Patrick Hughes
3
Nesse caso, eu criaria um módulo para esse aplicativo específico e o carregaria antes de executar os scripts (se estiver trabalhando interativamente) ou o carregaria dentro do script. Mas, de um modo geral, se você tiver um código específico apenas para uma determinada tarefa, desejará essas funções no script. Pessoalmente, apenas escrevo funções que genericamente fazem uma coisa. Se um pedaço de código é hiperespecializado, não faz muito sentido envolvê-lo em uma função ou módulo (a menos que haja vários scripts que usem o mesmo código, então pode fazer sentido).
JOEG
17
NÃO é necessário que o arquivo do módulo esteja em uma pasta com exatamente o mesmo nome que o arquivo PSM1. Isso pode ser feito como Import-Module .\buildsystem\PSUtils.psm1
Michael Freidgeim 23/03
2
@MichaelFreidgeim se é tão simples como apenas mudando o .com Import-Modulee renomear a extensão, e não requer os módulos para ser colocado em uma pasta específica, ou seja, eu posso tê-lo em qualquer diretório que eu quero, assim como com abastecimento ponto, é Existe alguma razão para fazer o sourcing de pontos nos módulos, considerando os benefícios que vêm para o escopo? (a não ser, claro, aqueles escopo "problemas" é o que você quer)
Abdul
2
@ Abdul, a fonte de pontos é mais simples, os módulos são muito mais poderosos. Veja stackoverflow.com/questions/14882332/…
Michael Freidgeim
17

. "$PSScriptRoot\MyFunctions.ps1" MyA1Func

Disponível a partir da v3, antes disso, consulte Como posso obter o local do sistema de arquivos de um script do PowerShell? . É MUITO comum.

PS Não assino a regra 'tudo é um módulo'. Meus scripts são usados ​​por outros desenvolvedores fora do GIT, portanto, não gosto de colocar coisas em um local específico ou modificar variáveis ​​de ambiente do sistema antes que meu script seja executado. É apenas um script (ou dois ou três).

yzorg
fonte
FWIW, você não precisa fazer nenhuma dessas coisas para executar o script em um módulo.
Nick Cox
@NickCox Eu adoraria ver alguns exemplos disso. Voc ~ e tem algum? +10 se o exemplo for de um projeto OSS. Especificamente, um exemplo de módulo PS sendo carregado por um caminho relativo (não PSModulePath ou sem customizar PSModulePath) e exemplo não trivial (ou seja, onde o módulo possui benefícios sobre o escopo normal do script).
yzorg 6/06/19
Eu frequentemente importo o módulo FluentMigrator.PowerShell de um caminho relativo. Isso permite verificar o controle de origem e garantir que todos estejam usando a mesma versão. Isso funciona bem.
Nick Cox
Não tenho certeza dos prós e contras relativos de empacotá-lo como um módulo versus como um script: talvez seja esse o caso de discutir com o autor? Eu acho que a capacidade de Get-Command -Module FluentMigrator.PowerShellser bastante agradável?
Nick Cox
@NickCox Você não qualificou totalmente o caminho do módulo nesse comando, o que significa que ele não seria encontrado, a menos que você copiasse o módulo em uma pasta global do módulo ou adicionasse a pasta GIT a uma variável de ambiente global. Eu acho que você acabou de demonstrar o meu ponto.
yzorg 12/06
7

Você certamente pode definir funções em arquivos de script (então eu tendem a carregá-las através do meu perfil do Powershell em carga).

Primeiro, você precisa verificar se a função está carregada executando:

ls function:\ | where { $_.Name -eq "A1"  }

E verifique se ele aparece na lista (deve ser uma lista de 1!), E deixe-nos saber qual é o resultado obtido!

Jonny
fonte
1
No PowerShell, a função é tratada como um diretório, portanto, é o mesmo que dizer c: \ ou d: \. Igualmente você funcionará sem a barra invertida, portanto ls function: | onde {$ _. Name -eq "A1"}
Jonny
4

Você pode adicionar uma função a:

c:\Users\David\Documents\WindowsPowerShell\profile.ps1

Uma função estará disponível.

David Morrow
fonte
3

Se o seu arquivo tiver apenas uma função principal que você deseja chamar / expor, também poderá iniciar o arquivo com:

Param($Param1)

Você pode chamá-lo, por exemplo, da seguinte maneira:

.\MyFunctions.ps1 -Param1 'value1'

Isso torna muito mais conveniente se você deseja chamar facilmente apenas essa função sem precisar importá-la.

Bergmeister
fonte
Também devo observar que descobri hoje (depois que um colega meu me contou) que o PowerShell adiciona automaticamente o [CmdletBinding()]atributo e o atualiza gratuitamente para uma função avançada. :-)
bergmeister
1

Supondo que você tenha um arquivo de módulo chamado Dummy-Name.psm1, que possui um método chamado Function-Dumb ()

Import-Module "Dummy-Name.psm1";
Get-Command -Module "Function-Dumb";
#
#
Function-Dumb;
Bytekoder
fonte