W programie PowerShell, jak zdefiniować funkcję w pliku i wywołać ją z wiersza polecenia programu PowerShell?

242

Mam plik .ps1, w którym chcę zdefiniować niestandardowe funkcje.

Wyobraź sobie, że plik nazywa się MyFunctions.ps1, a zawartość jest następująca:

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

Aby uruchomić ten skrypt i teoretycznie zarejestrować funkcję A1, przejdź do folderu, w którym znajduje się plik .ps1, i uruchom plik:

.\MyFunctions.ps1

To daje:

Installing functions
Done

Jednak gdy próbuję wywołać A1, po prostu pojawia się błąd stwierdzający, że nie ma polecenia / funkcji o tej nazwie:

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

Muszę źle zrozumieć niektóre koncepcje PowerShell. Czy nie mogę zdefiniować funkcji w plikach skryptów?

Zauważ, że ustawiłem już moją zasadę wykonania na „RemoteSigned”. Wiem, że mogę uruchamiać pliki .ps1 za pomocą kropki przed nazwą pliku:. \ MyFile.ps1

willem
źródło
Ładny link na temat ładowania funkcji przy starcie PS: sandfeld.net/powershell-load-your-functions-at-startup
Andrew

Odpowiedzi:

262

Wypróbuj to w wierszu poleceń PowerShell:

. .\MyFunctions.ps1
A1

Operator kropki służy do włączenia skryptu.

rsc
źródło
11
Oznacza to, że „uruchom to w bieżącym kontekście zamiast w kontekście potomnym”.
JasonMArcher
15
Oznacza źródło zawartości tego pliku. Taki sam jak w bash . ss64.com/bash/period.html
zapytanie
2
Wydaje się, że nie działa zbyt dobrze (przynajmniej z ISE), chyba że uruchomisz. \ MyFunctions.ps1, aby go udostępnić. Nie jestem pewien, czy działam ściśle z powershell.exe.
Mike Cheel,
1
Pomyślałem, że sprzeczne z intuicją jest to, że zbieranie kropek używa ścieżki w stosunku do pwd zamiast skryptu, więc zachęcam ludzi do szukania odpowiedzi JoeG i korzystania z modułów.
Spork
5
@Spork . "$PSScriptRoot\MyFunctions.ps1". Availalbe począwszy v3, przed tym zobaczyć stackoverflow.com/questions/3667238/... . To jest BARDZO powszechne.
yzorg,
232

To, o czym mówisz, nazywa się pozyskiwaniem kropek . I to jest złe. Ale nie martw się, istnieje lepszy i łatwiejszy sposób na robienie tego, co chcesz z modułami (brzmi to znacznie bardziej przerażająco niż jest). Główną zaletą korzystania z modułów jest to, że możesz je rozładować z powłoki, jeśli to konieczne, i powstrzymuje zmienne w funkcjach przed wkradaniem się do powłoki (po kropkowaniu źródła pliku funkcji spróbuj wywołać jedną ze zmiennych z funkcja w powłoce, a zobaczysz, co mam na myśli).

Najpierw zmień nazwę pliku .ps1, który zawiera wszystkie twoje funkcje, na MyFunctions.psm1 (właśnie utworzyłeś moduł!). Teraz, aby moduł ładował się poprawnie, musisz zrobić pewne określone rzeczy z plikiem. Najpierw moduł importujący widzi moduł (używasz tego polecenia cmdlet, aby załadować moduł do powłoki), musi on znajdować się w określonym miejscu. Domyślna ścieżka do folderu modułów to $ home \ Documents \ WindowsPowerShell \ Modules.

W tym folderze utwórz folder o nazwie MyFunctions i umieść w nim plik MyFunctions.psm1 (plik modułu musi znajdować się w folderze o dokładnie takiej samej nazwie jak plik PSM1).

Po zakończeniu otwórz PowerShell i uruchom następującą komendę:

Get-Module -listavailable

Jeśli widzisz jeden o nazwie MyFunctions, zrobiłeś to dobrze, a twój moduł jest gotowy do załadowania (to po prostu, aby upewnić się, że jest poprawnie skonfigurowany, musisz to zrobić tylko raz).

Aby użyć modułu, wpisz następujące polecenie w powłoce (lub umieść ten wiersz w swoim profilu $ lub umieść to jako pierwszy wiersz w skrypcie):

Import-Module MyFunctions

Możesz teraz uruchomić swoje funkcje. Fajną rzeczą jest to, że gdy masz już 10-15 funkcji, zapomnisz nazwy pary. Jeśli masz je w module, możesz uruchomić następującą komendę, aby uzyskać listę wszystkich funkcji w module:

Get-Command -module MyFunctions

Jest całkiem słodki, a niewielki wysiłek, jaki trzeba ustawić z przodu, jest WARTO.

JoeG
źródło
6
Co jeśli Twoje funkcje dotyczą tylko danej aplikacji PowerShell? Mam na myśli, że jeśli zainstalujesz pakiet PS1, aby wykonać jakieś zadanie, możesz nie chcieć wszystkich funkcji w swoim profilu, prawda?
Ian Patrick Hughes,
3
W takim przypadku utworzę moduł dla tej konkretnej aplikacji i albo załaduję go przed uruchomieniem skryptów (jeśli działa interaktywnie), albo załaduję go w skrypcie. Ale ogólnie mówiąc, jeśli masz kod, który jest specyficzny tylko dla danego zadania, potrzebujesz tych funkcji w skrypcie. Osobiście piszę tylko te funkcje, które generalnie robią jedną rzecz. Jeśli fragment kodu jest hiper wyspecjalizowany, naprawdę nie ma sensu zawijać go w funkcji lub module (chyba że istnieje kilka skryptów używających tego samego kodu, może to mieć sens).
JoeG
16
NIE jest konieczne, aby plik modułu znajdował się w folderze o dokładnie takiej samej nazwie jak plik PSM1. Można to zrobić jak Import-Module .\buildsystem\PSUtils.psm1
Michael Freidgeim
2
@MichaelFreidgeim jeśli jest to tak proste, jak tylko zmieniając .się Import-Modulei zmiany nazwy rozszerzenia, i nie wymaga moduły mają być umieszczone w folderze konkretnym, czyli mogę mieć go w dowolnym katalogu chcę, podobnie jak w przypadku dot sourcing, jest czy jest jakiś powód, aby nawet pozyskiwać kropki w modułach, biorąc pod uwagę korzyści płynące z określania zakresu? (chyba że oczywiście te „zakresy” zakresu są tym, czego chcesz)
Abdul
2
@Abdul, pozyskiwanie kropek jest prostsze, moduły są znacznie potężniejsze. Zobacz stackoverflow.com/questions/14882332/…
Michael Freidgeim,
17

. "$PSScriptRoot\MyFunctions.ps1" MyA1Func

Dostępne od wersji v3, wcześniej zobacz Jak mogę uzyskać lokalizację systemu plików skryptu PowerShell? . To jest BARDZO powszechne.

PS Nie zgadzam się z zasadą „wszystko jest modułem”. Moje skrypty są używane przez innych programistów poza GIT, więc nie lubię umieszczać rzeczy w określonym miejscu ani modyfikować zmiennych środowiskowych systemu przed uruchomieniem skryptu. To tylko skrypt (dwa lub trzy).

Yzorg
źródło
FWIW, nie musisz robić żadnej z tych rzeczy, aby uruchomić skrypt w module.
Nick Cox,
@NickCox Chciałbym zobaczyć kilka przykładów tego. Czy masz jakieś? +10, jeśli przykład pochodzi z projektu OSS. W szczególności przykład modułu PS ładowanego przez ścieżkę względną (nie PSModulePath lub bez dostosowywania PSModulePath) oraz nietrywialny przykład (tj. Gdzie moduł ma zalety w porównaniu do normalnego zakresu skryptów).
yzorg
Często importuję moduł FluentMigrator.PowerShell ze ścieżki względnej. To pozwala nam sprawdzić kontrolę źródła i upewnić się, że wszyscy używają tej samej wersji. To dobrze działa.
Nick Cox,
Nie jestem pewien względnych zalet i wad pakowania go jako modułu vs jako skryptu: może to jest temat do omówienia z autorem? Wydaje mi się, że brak Get-Command -Module FluentMigrator.PowerShelljest całkiem niezły?
Nick Cox,
@NickCox Nie podałeś w pełni ścieżki modułu w tym poleceniu, co oznacza, że ​​nie można go znaleźć, chyba że skopiujesz moduł do globalnego folderu modułu lub dodasz folder GIT do globalnej zmiennej środowiskowej. Myślę, że właśnie wykazałeś moją rację.
yzorg
7

Z pewnością możesz zdefiniować funkcje w plikach skryptów (następnie ładuję je przez mój profil Powershell).

Najpierw sprawdź, czy funkcja jest ładowana, uruchamiając:

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

I sprawdź, czy pojawia się na liście (powinna to być lista 1!), A następnie daj nam znać, jaki wynik otrzymasz!

Jonny
źródło
1
W programie PowerShell funkcja jest traktowana jak katalog, więc jest to to samo, co powiedzenie c: \ lub d: \. Tak samo będziesz działał bez ukośnika, więc funkcja ls: | gdzie {$ _. Nazwa -eq "A1"}
Jonny
4

Możesz dodać funkcję do:

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

Funkcja będzie dostępna.

David Morrow
źródło
3

Jeśli plik ma tylko jedną główną funkcję, którą chcesz wywołać / ujawnić, możesz również uruchomić plik za pomocą:

Param($Param1)

Możesz to nazwać np. W następujący sposób:

.\MyFunctions.ps1 -Param1 'value1'

To sprawia, że ​​jest znacznie wygodniej, jeśli chcesz łatwo wywołać tylko tę funkcję bez konieczności importowania funkcji.

bergmeister
źródło
Powinienem również zauważyć, że dzisiaj odkryłem (po tym, jak mój kolega powiedział mi), że PowerShell automatycznie dodaje [CmdletBinding()]atrybut i aktualizuje go za darmo do zaawansowanej funkcji. :-)
bergmeister
1

Zakładając, że masz plik modułu o nazwie Dummy-Name.psm1, który zawiera metodę o nazwie Function-Dumb ()

Import-Module "Dummy-Name.psm1";
Get-Command -Module "Function-Dumb";
#
#
Function-Dumb;
Bytekoder
źródło