In PowerShell, come posso definire una funzione in un file e chiamarla dalla riga di comando di PowerShell?


243

Ho un file .ps1 in cui voglio definire funzioni personalizzate.

Immagina che il file si chiami MyFunctions.ps1 e il contenuto sia il seguente:

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

Per eseguire questo script e teoricamente registrare la funzione A1, navigo nella cartella in cui risiede il file .ps1 ed eseguo il file:

.\MyFunctions.ps1

Questo produce:

Installing functions
Done

Tuttavia, quando provo a chiamare A1, ottengo semplicemente l'errore che indica che non esiste alcun comando / funzione con quel 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 fraintendere alcuni concetti di PowerShell. Non posso definire le funzioni nei file di script?

Nota che ho già impostato la mia politica di esecuzione su "RemoteSigned". E so di eseguire file .ps1 usando un punto davanti al nome del file:. \ MyFile.ps1


Bel link sul caricamento delle funzioni all'avvio di PS: sandfeld.net/powershell-load-your-functions-at-startup
Andrew

Risposte:


262

Prova questo dalla riga di comando di PowerShell:

. .\MyFunctions.ps1
A1

L'operatore punto viene utilizzato per includere lo script.


11
Bene, significa "esegui questo nel contesto corrente anziché in un contesto figlio".
JasonMArcher,

15
Significa fonte del contenuto di questo file. Come in bash . ss64.com/bash/period.html
inquam

2
Non sembra funzionare molto bene (almeno da ISE) a meno che tu non esegua. \ MyFunctions.ps1 prima di renderlo disponibile. Non sono sicuro di eseguire rigorosamente da powershell.exe.
Mike Cheel,

1
Pensavo fosse controintuitivo che il dot-sourcing usasse il percorso relativo alla pwd piuttosto che alla sceneggiatura, quindi avrei esortato le persone a guardare invece la risposta di JoeG e ad usare i moduli.
Spork,

5
@Spork . "$PSScriptRoot\MyFunctions.ps1". Disponibile a partire dalla v3, prima vedi stackoverflow.com/questions/3667238/… . È MOLTO comune.
yzorg,

233

Quello di cui stai parlando si chiama dot sourcing . Ed è malvagio. Ma non preoccuparti, c'è un modo migliore e più semplice per fare ciò che vuoi con i moduli (sembra molto più spaventoso di quello che è). Il vantaggio principale dell'utilizzo dei moduli è che è possibile scaricarli dalla shell, se necessario, e impedisce alle variabili nelle funzioni di insinuarsi nella shell (una volta puntato un file di funzioni, provare a chiamare una delle variabili da un funzione nella shell e vedrai cosa intendo).

Quindi, prima di tutto, rinomina il file .ps1 che contiene tutte le tue funzioni in MyFunctions.psm1 (hai appena creato un modulo!). Ora che un modulo si carichi correttamente, devi fare alcune cose specifiche con il file. Innanzitutto per Import-Module per vedere il modulo (si utilizza questo cmdlet per caricare il modulo nella shell), deve trovarsi in una posizione specifica. Il percorso predefinito per la cartella dei moduli è $ home \ Documents \ WindowsPowerShell \ Modules.

In quella cartella, crea una cartella denominata MyFunctions e inserisci il file MyFunctions.psm1 (il file del modulo deve risiedere in una cartella con esattamente lo stesso nome del file PSM1).

Una volta fatto ciò, apri PowerShell ed esegui questo comando:

Get-Module -listavailable

Se ne vedi uno chiamato MyFunctions, l'hai fatto bene e il tuo modulo è pronto per essere caricato (questo è solo per assicurarti che sia impostato correttamente, devi farlo solo una volta).

Per utilizzare il modulo, digitare quanto segue nella shell (o inserire questa riga nel proprio profilo $ oppure inserirla come prima riga in uno script):

Import-Module MyFunctions

Ora puoi eseguire le tue funzioni. La cosa bella di questo è che una volta che hai 10-15 funzioni lì dentro, dimenticherai il nome di una coppia. Se li hai in un modulo, puoi eseguire il comando seguente per ottenere un elenco di tutte le funzioni nel tuo modulo:

Get-Command -module MyFunctions

È piuttosto dolce, e il piccolo sforzo che ci vuole per allestire sul lato anteriore ne vale la pena.


6
Che dire se le tue funzioni sono pertinenti solo a quella determinata applicazione PowerShell? Voglio dire, se installi un pacchetto di PS1 per fare un lavoro da qualche parte, potresti non voler tutte le funzioni nel tuo profilo, giusto?
Ian Patrick Hughes,

3
In tal caso, creerei un modulo per quella specifica applicazione e lo caricarei prima di eseguire gli script (se funzionava in modo interattivo), oppure lo avrei caricato all'interno dello script. Ma in generale se si dispone di un codice specifico solo per una determinata attività, si vorrebbero quelle funzioni nello script. Personalmente scrivo solo funzioni che genericamente fanno una cosa. Se un pezzo di codice è iper-specializzato, non ha davvero senso racchiuderlo in una funzione o in un modulo (a meno che non ci siano diversi script che usano lo stesso codice, potrebbe avere senso).
JoeG,

17
NON è necessario che il file del modulo si trovi in ​​una cartella con esattamente lo stesso nome del file PSM1. Può essere fatto come Import-Module .\buildsystem\PSUtils.psm1
Michael Freidgeim,

2
@MichaelFreidgeim se è semplice come cambiare il .con Import-Modulee rinominare l'estensione, e non richiede che i moduli siano collocati in una cartella specifica, cioè posso averlo in qualsiasi directory che voglio, proprio come con dot sourcing, è c'è qualche motivo per fare dot sourcing sui moduli, considerando i vantaggi che derivano dallo scoping? (a meno che, naturalmente, questi "problemi" non siano quello che vuoi)
Abdul

2
@Abdul, dot sourcing è più semplice, i moduli sono molto più potenti. Vedere stackoverflow.com/questions/14882332/...
Michael Freidgeim

17

. "$PSScriptRoot\MyFunctions.ps1" MyA1Func

Availalbe a partire dalla v3, prima che vedi Come posso ottenere il percorso del file system di uno script PowerShell? . È MOLTO comune.

PS Non sottoscrivo la regola "tutto è un modulo". I miei script sono utilizzati da altri sviluppatori di GIT, quindi non mi piace inserire elementi in un luogo specifico o modificare le variabili di ambiente di sistema prima che il mio script venga eseguito. È solo una sceneggiatura (o due o tre).


FWIW, non devi fare nessuna di queste cose per eseguire script in un modulo.
Nick Cox,

@NickCox Mi piacerebbe vedere alcuni esempi di questo. Hai qualche? +10 se l'esempio proviene da un progetto OSS. In particolare, un esempio di modulo PS che viene caricato tramite un percorso relativo (non PSModulePath o senza personalizzazione di PSModulePath) ed esempio non banale (ovvero dove il modulo presenta vantaggi rispetto al normale ambito di script).
yzorg,

Importare frequentemente il modulo FluentMigrator.PowerShell da un percorso relativo. Ciò ci consente di controllarlo nel controllo del codice sorgente e di garantire che tutti utilizzino la stessa versione. Funziona bene.
Nick Cox,

Non sono sicuro dei relativi pro e contro di impacchettarlo come modulo rispetto a come una sceneggiatura: forse è quello di cui discutere con l'autore? Immagino che l'abilità Get-Command -Module FluentMigrator.PowerShellsia abbastanza bella?
Nick Cox,

@NickCox Non hai qualificato completamente il percorso del modulo in quel comando, il che significa che non sarebbe possibile trovarlo a meno che tu non copiassi il modulo in una cartella del modulo globale o aggiungessi la tua cartella GIT a una variabile d'ambiente globale. Penso che tu abbia appena dimostrato il mio punto.
yzorg,

7

Puoi certamente definire le funzioni nei file di script (tendo a caricarle sul mio profilo Powershell al caricamento).

Innanzitutto è necessario verificare per assicurarsi che la funzione sia caricata eseguendo:

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

E controlla che appaia nell'elenco (dovrebbe essere un elenco di 1!), Quindi facci sapere quale output ottieni!


1
In PowerShell la funzione è considerata come una directory, quindi equivale a dire c: \ o d: \. Allo stesso modo funzionerà senza la barra rovesciata, quindi ls funzione: | dove {$ _. Nome -eq "A1"}
Jonny

4

È possibile aggiungere la funzione a:

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

Una funzione sarà disponibile.


3

Se il tuo file ha solo una funzione principale che desideri chiamare / esporre, puoi anche avviare il file con:

Param($Param1)

È quindi possibile chiamarlo ad esempio come segue:

.\MyFunctions.ps1 -Param1 'value1'

Questo lo rende molto più conveniente se si desidera chiamare facilmente solo quella funzione senza dover importare la funzione.


Dovrei anche notare che ho scoperto oggi (dopo che un mio collega mi ha detto) che PowerShell aggiunge automaticamente l' [CmdletBinding()]attributo e lo aggiorna gratuitamente a una funzione avanzata. :-)
bergmeister

1

Supponendo di avere un file di modulo chiamato Dummy-Name.psm1 che ha un metodo chiamato Function-Dumb ()

Import-Module "Dummy-Name.psm1";
Get-Command -Module "Function-Dumb";
#
#
Function-Dumb;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.