Di PowerShell, bagaimana cara menetapkan fungsi dalam file dan memanggilnya dari commandline PowerShell?

243

Saya memiliki file .ps1 di mana saya ingin mendefinisikan fungsi khusus.

Bayangkan file tersebut bernama MyFunctions.ps1, dan isinya adalah sebagai berikut:

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

Untuk menjalankan skrip ini dan secara teoritis mendaftarkan fungsi A1, saya menavigasi ke folder tempat file .ps1 berada dan menjalankan file:

.\MyFunctions.ps1

Output ini:

Installing functions
Done

Namun, ketika saya mencoba menelepon A1, saya hanya mendapatkan kesalahan yang menyatakan bahwa tidak ada perintah / fungsi dengan nama itu:

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

Saya harus salah paham beberapa konsep PowerShell. Bisakah saya tidak mendefinisikan fungsi dalam file skrip?

Perhatikan bahwa saya telah menetapkan kebijakan eksekusi ke 'RemoteSigned'. Dan saya tahu untuk menjalankan file .ps1 menggunakan titik di depan nama file:. \ MyFile.ps1

Willem
sumber
Tautan yang bagus untuk memuat fungsi di startup PS: sandfeld.net/powershell-load-your-functions-at-startup
Andrew

Jawaban:

262

Coba ini di baris perintah PowerShell:

. .\MyFunctions.ps1
A1

Operator titik digunakan untuk skrip termasuk.

rsc
sumber
11
Ya, itu berarti "jalankan ini dalam konteks saat ini daripada konteks anak."
JasonMArcher
15
Itu berarti sumber isi dari file ini. Sama seperti di bash . ss64.com/bash/ Period.html
inquam
2
Tampaknya tidak berfungsi dengan baik (setidaknya dari ISE) kecuali Anda menjalankan. \ MyFunctions.ps1 terlebih dahulu untuk membuatnya tersedia. Saya tidak yakin tentang menjalankan secara ketat dari powershell.exe.
Mike Cheel
1
Saya pikir itu kontra-intuitif bahwa dot-sourcing menggunakan path relatif ke pwd daripada skrip, jadi saya akan mendorong orang untuk melihat jawaban JoeG sebagai gantinya dan menggunakan modul.
Spork
5
@Spork . "$PSScriptRoot\MyFunctions.ps1". Availalbe dimulai pada v3, sebelum itu lihat stackoverflow.com/questions/3667238/… . Ini SANGAT umum.
yzorg
233

Apa yang Anda bicarakan disebut dot sourcing . Dan itu jahat. Tapi jangan khawatir, ada cara yang lebih baik dan lebih mudah untuk melakukan apa yang Anda inginkan dengan modul (kedengarannya jauh lebih menakutkan daripada itu). Manfaat utama menggunakan modul adalah Anda dapat membongkar modul-modul tersebut dari shell jika perlu, dan itu membuat variabel-variabel di dalam fungsi tidak masuk ke dalam shell (begitu Anda dot sumber file fungsi, coba panggil salah satu variabel dari berfungsi dalam shell, dan Anda akan melihat apa yang saya maksud).

Jadi pertama-tama, ganti nama file .ps1 yang memiliki semua fungsi Anda di dalamnya menjadi MyFunctions.psm1 (Anda baru saja membuat modul!). Sekarang agar modul memuat dengan benar, Anda harus melakukan beberapa hal spesifik dengan file tersebut. Pertama untuk Modul Impor untuk melihat modul (Anda menggunakan cmdlet ini untuk memuat modul ke dalam shell), itu harus di lokasi tertentu. Path default ke folder modules adalah $ home \ Documents \ WindowsPowerShell \ Modules.

Dalam folder itu, buat folder bernama MyFunctions, dan tempatkan file MyFunctions.psm1 ke dalamnya (file modul harus berada di folder dengan nama yang persis sama dengan file PSM1).

Setelah selesai, buka PowerShell, dan jalankan perintah ini:

Get-Module -listavailable

Jika Anda melihat yang disebut MyFunctions, Anda melakukannya dengan benar, dan modul Anda siap dimuat (ini hanya untuk memastikan bahwa ini diatur dengan benar, Anda hanya perlu melakukan ini satu kali).

Untuk menggunakan modul, ketikkan yang berikut di shell (atau letakkan baris ini di $ profile Anda, atau letakkan ini sebagai baris pertama dalam skrip):

Import-Module MyFunctions

Anda sekarang dapat menjalankan fungsi Anda. Yang keren tentang ini adalah bahwa sekali Anda memiliki 10-15 fungsi di sana, Anda akan melupakan nama pasangan. Jika Anda memilikinya di dalam modul, Anda dapat menjalankan perintah berikut untuk mendapatkan daftar semua fungsi di modul Anda:

Get-Command -module MyFunctions

Ini cukup manis, dan sedikit usaha yang diperlukan untuk mengatur di sisi depan adalah WAY worth it.

JoeG
sumber
6
Bagaimana jika fungsi Anda hanya berkaitan dengan aplikasi PowerShell yang diberikan? Maksud saya, jika Anda menginstal paket PS1 untuk melakukan pekerjaan di suatu tempat, Anda mungkin tidak ingin setiap fungsi di profil Anda, bukan?
Ian Patrick Hughes
3
Dalam hal ini, saya akan membuat modul untuk aplikasi spesifik itu, dan memuatnya sebelum menjalankan skrip (jika bekerja secara interaktif), atau memuatnya dalam skrip. Tetapi secara umum jika Anda memiliki kode yang spesifik hanya untuk tugas yang diberikan, Anda ingin fungsi-fungsi tersebut dalam skrip. Secara pribadi saya hanya menulis fungsi yang secara umum melakukan satu hal. Jika sepotong kode adalah hyper khusus, itu tidak masuk akal untuk membungkusnya dalam suatu fungsi atau modul (kecuali ada beberapa skrip yang menggunakan kode yang sama, maka mungkin masuk akal).
JoeG
17
Ini TIDAK perlu file modul berada di folder dengan nama yang persis sama dengan file PSM1. Itu bisa dilakukan seperti Import-Module .\buildsystem\PSUtils.psm1
Michael Freidgeim
2
@MichaelFreidgeim jika sesederhana hanya .dengan Import-Modulemengubah dan mengganti nama ekstensi, dan tidak memerlukan modul untuk ditempatkan di folder tertentu, yaitu saya dapat memilikinya di direktori yang saya inginkan, seperti halnya dengan dot sourcing, adalah apakah ada alasan untuk melakukan dot sourcing melalui modul, mengingat manfaat yang datang untuk pelingkupan? (kecuali tentu saja "masalah" ruang lingkup itu yang Anda inginkan)
Abdul
2
@ Abdul, sumber dot lebih sederhana, modul jauh lebih kuat. Lihat stackoverflow.com/questions/14882332/…
Michael Freidgeim
17

. "$PSScriptRoot\MyFunctions.ps1" MyA1Func

Availalbe dimulai pada v3, sebelum itu lihat Bagaimana saya bisa mendapatkan lokasi sistem file dari skrip PowerShell? . Ini SANGAT umum.

PS Saya tidak berlangganan aturan 'semuanya modul'. Skrip saya digunakan oleh pengembang lain di luar GIT, jadi saya tidak suka meletakkan barang di tempat tertentu atau mengubah variabel lingkungan sistem sebelum skrip saya akan berjalan. Itu hanya sebuah skrip (atau dua, atau tiga).

yzorg
sumber
FWIW, Anda tidak perlu melakukan hal-hal itu untuk menjalankan skrip dalam sebuah modul.
Nick Cox
@NickCox Saya ingin melihat beberapa contohnya. Apakah Anda memiliki? +10 jika contohnya dari proyek OSS. Khususnya, contoh modul PS yang dimuat melalui jalur relatif (bukan PSModulePath atau tanpa menyesuaikan PSModulePath), dan contoh non-sepele (yaitu di mana modul memiliki manfaat dibandingkan pelingkupan skrip normal).
yzorg
Saya sering mengimpor modul FluentMigrator.PowerShell dari jalur relatif. Itu memungkinkan kita memeriksanya ke dalam kontrol sumber dan memastikan bahwa semua orang menggunakan versi yang sama. Ini bekerja dengan baik.
Nick Cox
Saya tidak yakin tentang pro dan kontra relatif dari pengemasannya sebagai modul vs sebagai skrip: mungkin itu yang harus dibicarakan dengan penulis? Saya kira kemampuannya Get-Command -Module FluentMigrator.PowerShellcukup bagus?
Nick Cox
@NickCox Anda tidak sepenuhnya memenuhi syarat jalur modul dalam perintah itu, yang berarti tidak akan ditemukan kecuali Anda menyalin modul ke folder modul global atau menambahkan folder GIT Anda ke variabel lingkungan global. Saya pikir Anda baru saja menunjukkan maksud saya.
yzorg
7

Anda tentu dapat mendefinisikan fungsi dalam file skrip (saya kemudian cenderung memuatnya melalui profil Powershell saya saat dimuat).

Pertama, Anda perlu memeriksa untuk memastikan fungsi dimuat dengan menjalankan:

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

Dan periksa apakah itu muncul dalam daftar (harus daftar 1!), Lalu beri tahu kami hasil yang Anda dapatkan!

Jonny
sumber
1
Dalam fungsi PowerShell diperlakukan sebagai direktori sehingga sama dengan mengatakan c: \ atau d: \. Sama Anda akan bekerja tanpa garis miring terbalik sehingga berfungsi: | di mana {$ _. Nama -eq "A1"}
Jonny
4

Anda dapat menambahkan fungsi ke:

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

Sebuah fungsi akan tersedia.

David Morrow
sumber
3

Jika file Anda hanya memiliki satu fungsi utama yang ingin Anda panggil / paparkan, maka Anda juga dapat memulai file dengan:

Param($Param1)

Anda kemudian dapat menyebutnya misalnya sebagai berikut:

.\MyFunctions.ps1 -Param1 'value1'

Ini membuatnya jauh lebih nyaman jika Anda ingin dengan mudah memanggil fungsi itu tanpa harus mengimpor fungsi.

bergmeister
sumber
Saya juga harus mencatat bahwa saya menemukan hari ini (setelah seorang kolega saya memberi tahu saya) bahwa PowerShell secara otomatis menambahkan [CmdletBinding()]atribut dan memutakhirkannya secara gratis ke fungsi lanjutan. :-)
bergmeister
1

Dengan asumsi Anda memiliki file modul bernama Dummy-Name.psm1 yang memiliki metode yang disebut Function-Dumb ()

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