Sdílet prostřednictvím


Kapitola 9 – funkce

Jednořádkové skripty a skripty PowerShellu, které je potřeba často upravovat, jsou vhodnými kandidáty na přeměnu na opakovaně použitelné funkce.

Kdykoli je to možné, můžete psát funkce, protože jsou více orientované na nástroje. Funkce můžete přidat do modulu skriptu, umístit tento modul do umístění definovaného v $env:PSModulePatha volat funkce, aniž byste museli najít, kam jste funkce uložili. Pomocí modulu PowerShellGet je snadné sdílet moduly PowerShellu v úložišti NuGet. PowerShellGet se dodává s PowerShellem ve verzi 5.0 a vyšší. Je také k dispozici jako samostatný soubor ke stažení pro PowerShell verze 3.0 a vyšší.

Nepřekombinujte věci. Udržujte to jednoduché a používejte nejjednodušší způsob, jak provést úkol. Vyhněte se aliasům a pozičním parametrům v libovolném kódu, který znovu použijete. Naformátujte kód tak, aby byl čitelný. Nezakódujte hodnoty; použít parametry a proměnné. Nepište nepotřebný kód, i když nic neublíží. Přidává zbytečné složitosti. Při psaní jakéhokoli kódu PowerShellu je potřeba věnovat pozornost podrobnostem.

Pojmenování

Při pojmenování funkcí v PowerShellu použijte Pascal case název se schváleným slovesem a jednotným podstatným jménem. Pokud chcete získat seznam schválených příkazů v PowerShellu, spusťte Get-Verb. Následující příklad seřadí výsledky podle vlastnosti Sloveso u Get-Verb.

Get-Verb | Sort-Object -Property Verb

Vlastnost Group vám poskytne představu o tom, jak se mají příkazy použít.

Verb        Group
----        -----
Add         Common
Approve     Lifecycle
Assert      Lifecycle
Backup      Data
Block       Security
Checkpoint  Data
Clear       Common
Close       Common
Compare     Data
Complete    Lifecycle
Compress    Data
Confirm     Lifecycle
Connect     Communications
Convert     Data
ConvertFrom Data
ConvertTo   Data
Copy        Common
Debug       Diagnostic
Deny        Lifecycle
Disable     Lifecycle
Disconnect  Communications
Dismount    Data
Edit        Data
Enable      Lifecycle
Enter       Common
Exit        Common
Expand      Data
Export      Data
Find        Common
Format      Common
Get         Common
Grant       Security
Group       Data
Hide        Common
Import      Data
Initialize  Data
Install     Lifecycle
Invoke      Lifecycle
Join        Common
Limit       Data
Lock        Common
Measure     Diagnostic
Merge       Data
Mount       Data
Move        Common
New         Common
Open        Common
Optimize    Common
Out         Data
Ping        Diagnostic
Pop         Common
Protect     Security
Publish     Data
Push        Common
Read        Communications
Receive     Communications
Redo        Common
Register    Lifecycle
Remove      Common
Rename      Common
Repair      Diagnostic
Request     Lifecycle
Reset       Common
Resize      Common
Resolve     Diagnostic
Restart     Lifecycle
Restore     Data
Resume      Lifecycle
Revoke      Security
Save        Data
Search      Common
Select      Common
Send        Communications
Set         Common
Show        Common
Skip        Common
Split       Common
Start       Lifecycle
Step        Common
Stop        Lifecycle
Submit      Lifecycle
Suspend     Lifecycle
Switch      Common
Sync        Data
Test        Diagnostic
Trace       Diagnostic
Unblock     Security
Undo        Common
Uninstall   Lifecycle
Unlock      Common
Unprotect   Security
Unpublish   Data
Unregister  Lifecycle
Update      Data
Use         Other
Wait        Lifecycle
Watch       Common
Write       Communications

Pro funkce PowerShellu je důležité použít schválené příkazy. Moduly, které obsahují funkce s neschválenými příkazy, generují při importu do relace PowerShellu zprávu s upozorněním. Tato zpráva s upozorněním způsobí, že vaše funkce vypadají neprofesionálně. Neschválené příkazy také omezují zjistitelnost vašich funkcí.

Jednoduchá funkce

Funkce v PowerShellu se deklaruje s klíčovým slovem funkce následovaným názvem funkce a potom levou a pravou složenou závorkou ({ }). Kód spuštěný funkcí je obsažen v těchto složených závorkách.

function Get-Version {
    $PSVersionTable.PSVersion
}

Funkce zobrazená v následujícím příkladu je jednoduchý příklad, který vrací verzi PowerShellu.

Get-Version
Major  Minor  Build  Revision
-----  -----  -----  --------
5      1      14393  693

Pokud pro funkce použijete obecný název, například Get-Version, může dojít ke konfliktům pojmenování. Výchozí příkazy přidané v budoucnu nebo příkazy, které můžou napsat ostatní, můžou být v konfliktu s nimi. Přidejte předponu k podstatné části jmen vašich funkcí, abyste předešli konfliktům názvů. Příklad: <ApprovedVerb>-<Prefix><SingularNoun>.

Následující příklad používá předponu PS.

function Get-PSVersion {
    $PSVersionTable.PSVersion
}

Kromě názvu je tato funkce shodná s předchozím názvem.

Get-PSVersion
Major  Minor  Build  Revision
-----  -----  -----  --------
5      1      14393  693

Konflikt názvů můžete mít i po přidání předpony ke podstatnému jménu. Rád přidávám před své názvy funkcí své iniciály. Vyvíjejte standard a držte se ho.

function Get-MrPSVersion {
    $PSVersionTable.PSVersion
}

Tato funkce se neliší od předchozích dvou, s výjimkou použití jedinečnějšího názvu, aby se zabránilo konfliktům názvů s jinými příkazy PowerShellu.

Get-MrPSVersion
Major  Minor  Build  Revision
-----  -----  -----  --------
5      1      14393  693

Po načtení do paměti uvidíte funkce na Function PSDrive.

Get-ChildItem -Path Function:\Get-*Version
CommandType     Name                                               Version
-----------     ----                                               -------
Function        Get-Version
Function        Get-PSVersion
Function        Get-MrPSVersion

Pokud chcete tyto funkce odebrat z aktuální relace, odeberte je z Function PSDrive nebo zavřete PowerShell a znovu ho otevřete.

Get-ChildItem -Path Function:\Get-*Version | Remove-Item

Ověřte, že funkce byly skutečně odebrány.

Get-ChildItem -Path Function:\Get-*Version

Pokud byly funkce načteny jako součást modulu, můžete modul uvolnit, abyste je odebrali.

Remove-Module -Name <ModuleName>

Cmdlet Remove-Module odebere moduly PowerShellu z paměti v aktuální relaci PowerShellu. Neodebere je ze systému nebo disku.

Parametry

Nepřiřazujte hodnoty staticky. Místo toho použijte parametry a proměnné. Při pojmenování parametrů použijte stejný název jako výchozí rutiny pro názvy parametrů, kdykoli je to možné.

V následující funkci si všimněte, že jsem použil ComputerName, a nikoli Computer, ServerNamenebo Host pro název parametru. Pomocí ComputerName standardizuje název parametru tak, aby odpovídal názvu parametru a případu, jako jsou výchozí rutiny.

function Test-MrParameter {

    param (
        $ComputerName
    )

    Write-Output $ComputerName

}

Následující funkce se dotazuje na všechny příkazy ve vašem systému a vrátí číslo s konkrétními názvy parametrů.

function Get-MrParameterCount {
    param (
        [string[]]$ParameterName
    )

    foreach ($Parameter in $ParameterName) {
        $Results = Get-Command -ParameterName $Parameter -ErrorAction SilentlyContinue

        [pscustomobject]@{
            ParameterName   = $Parameter
            NumberOfCmdlets = $Results.Count
        }
    }
}

Jak vidíte v následujících výsledcích, 39 příkazů, které mají ComputerName parametr. Nejsou k dispozici žádné příkazy s parametry, jako jsoupočítače , název_serveru,hostitele nebopočítače .

Get-MrParameterCount -ParameterName ComputerName, Computer, ServerName,
    Host, Machine
ParameterName NumberOfCmdlets
------------- ---------------
ComputerName               39
Computer                    0
ServerName                  0
Host                        0
Machine                     0

Použijte stejnou velikost písmen pro názvy parametrů jako ve výchozích rutinách cmdletů. Například použijte ComputerName, nikoli computername. Toto schéma pojmenování pomáhá lidem obeznámeným s PowerShellem rozeznat vaše funkce a působí dojmem výchozích rutin (cmdletů).

Příkaz param umožňuje definovat jeden nebo více parametrů. Čárka (,) odděluje definice parametrů. Další informace naleznete v části about_Functions_Advanced_Parameters.

Pokročilé funkce

Přeměna funkce na pokročilou funkci v PowerShellu je jednoduchá. Jedním z rozdílů mezi funkcí a pokročilou funkcí je, že pokročilé funkce mají společné parametry, které se automaticky přidají. Mezi běžné parametry patří parametry, jako jsou podrobné a ladění.

Začněte funkcí Test-MrParameter, která byla použita v předchozí části.

function Test-MrParameter {

    param (
        $ComputerName
    )

    Write-Output $ComputerName

}

Existuje několik různých způsobů, jak zobrazit běžné parametry. Jedním ze způsobů je prohlížení syntaxe pomocí Get-Command.

Get-Command -Name Test-MrParameter -Syntax

Všimněte si, že funkce Test-MrParameter nemá žádné společné parametry.

Test-MrParameter [[-ComputerName] <Object>]

Další možností je přejít k podrobnostem o vlastnosti parametrů Get-Command.

(Get-Command -Name Test-MrParameter).Parameters.Keys
ComputerName

Přidejte atribut CmdletBinding, který převede funkci na pokročilou funkci.

function Test-MrCmdletBinding {

    [CmdletBinding()] # Turns a regular function into an advanced function
    param (
        $ComputerName
    )

    Write-Output $ComputerName

}

Při zadávání CmdletBindingse běžné parametry přidají automaticky. CmdletBinding vyžaduje blok param, ale blok param může být prázdný.

Get-Command -Name Test-MrCmdletBinding -Syntax
Test-MrCmdletBinding [[-ComputerName] <Object>] [<CommonParameters>]

Detailní procházení vlastnosti parametrů Get-Command zobrazuje skutečné názvy parametrů, včetně těch běžně používaných.

(Get-Command -Name Test-MrCmdletBinding).Parameters.Keys
ComputerName
Verbose
Debug
ErrorAction
WarningAction
InformationAction
ErrorVariable
WarningVariable
InformationVariable
OutVariable
OutBuffer
PipelineVariable

PodporujeShouldProcess

Atribut SupportsShouldProcess přidá parametry zmírnění rizik WhatIf a Confirm. Tyto parametry jsou potřeba jenom pro příkazy, které provádějí změny.

function Test-MrSupportsShouldProcess {

    [CmdletBinding(SupportsShouldProcess)]
    param (
        $ComputerName
    )

    Write-Output $ComputerName

}

Všimněte si, že teď jsou parametry WhatIf a Confirm.

Get-Command -Name Test-MrSupportsShouldProcess -Syntax
Test-MrSupportsShouldProcess [[-ComputerName] <Object>] [-WhatIf] [-Confirm]
[<CommonParameters>]

Znovu můžete také použít Get-Command k vrácení seznamu skutečných názvů parametrů, včetně běžných parametrů, a dalších jako WhatIf a Potvrdit.

(Get-Command -Name Test-MrSupportsShouldProcess).Parameters.Keys
ComputerName
Verbose
Debug
ErrorAction
WarningAction
InformationAction
ErrorVariable
WarningVariable
InformationVariable
OutVariable
OutBuffer
PipelineVariable
WhatIf
Confirm

Ověření parametru

Nejdřív ověřte vstup. Nepovolujte kódu pokračovat v cestě, když se nedá dokončit bez platného vstupu.

Vždy zadejte datový typ pro proměnné použité pro parametry. V následujícím příkladu je String zadán jako datový typ pro parametr ComputerName. Toto ověření omezuje, aby bylo možné zadat pouze jeden název počítače pro parametr ComputerName.

function Test-MrParameterValidation {

    [CmdletBinding()]
    param (
        [string]$ComputerName
    )

    Write-Output $ComputerName

}

Pokud je zadáno více než jeden název počítače, vygeneruje se chyba.

Test-MrParameterValidation -ComputerName Server01, Server02
Test-MrParameterValidation : Cannot process argument transformation on
parameter 'ComputerName'. Cannot convert value to type System.String.
At line:1 char:42
+ Test-MrParameterValidation -ComputerName Server01, Server02
+                                          ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Test-MrParameterValidation]
   , ParameterBindingArgumentTransformationException
    + FullyQualifiedErrorId : ParameterArgumentTransformationError,Test-MrP
   arameterValidation

Problém s aktuální definicí je, že je možné vynechat hodnotu parametru ComputerName, ale hodnota je potřebná k úspěšnému dokončení funkce. V tomto scénáři je přínosný atribut parametru Mandatory.

Syntaxe použitá v následujícím příkladu je kompatibilní s PowerShellem verze 3.0 a vyšší. [Parameter(Mandatory=$true)] je možné zadat, aby byla funkce kompatibilní s PowerShellem verze 2.0 nebo vyšší.

function Test-MrParameterValidation {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]$ComputerName
    )

    Write-Output $ComputerName

}

Nyní, když je ComputerName vyžadován, funkce vyzve k jeho zadání, pokud není uveden.

Test-MrParameterValidation
cmdlet Test-MrParameterValidation at command pipeline position 1
Supply values for the following parameters:
ComputerName:

Pokud chcete pro parametr ComputerName povolit více než jednu hodnotu, použijte datový typ String, ale přidejte do datového typu hranaté závorky ([]), abyste umožnili pole řetězců.

function Test-MrParameterValidation {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string[]]$ComputerName
    )

    Write-Output $ComputerName

}

Možná chcete zadat výchozí hodnotu pro parametr ComputerName, pokud není zadaný. Problémem je, že výchozí hodnoty nelze použít s povinnými parametry. Místo toho použijte atribut ověření parametru ValidateNotNullOrEmpty s výchozí hodnotou.

I když nastavíte výchozí hodnotu, zkuste nepoužívat statické hodnoty. V následujícím příkladu se $env:COMPUTERNAME použije jako výchozí hodnota, která se automaticky přeloží na název místního počítače, pokud není zadaná hodnota.

function Test-MrParameterValidation {

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [string[]]$ComputerName = $env:COMPUTERNAME
    )

    Write-Output $ComputerName

}

Podrobný výstup

Vložené komentáře jsou užitečné, pokud píšete složitý kód, ale uživatelé je neuvidí, pokud se na kód nedívají.

Funkce v následujícím příkladu obsahuje komentář přímo ve smyčce foreach. I když tento konkrétní komentář nemusí být obtížné najít, představte si, jestli funkce obsahovala stovky řádků kódu.

function Test-MrVerboseOutput {

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [string[]]$ComputerName = $env:COMPUTERNAME
    )

    foreach ($Computer in $ComputerName) {
        #Attempting to perform an action on $Computer <<-- Don't use
        #inline comments like this, use write verbose instead.
        Write-Output $Computer
    }

}

Lepší možností je místo vložených komentářů použít Write-Verbose.

function Test-MrVerboseOutput {

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [string[]]$ComputerName = $env:COMPUTERNAME
    )

    foreach ($Computer in $ComputerName) {
        Write-Verbose -Message "Attempting to perform an action on $Computer"
        Write-Output $Computer
    }

}

Podrobný výstup se nezobrazí, pokud je funkce volána bez parametru Podrobný.

Test-MrVerboseOutput -ComputerName Server01, Server02

Podrobný výstup se zobrazí, když je funkce volána pomocí parametru Verbose.

Test-MrVerboseOutput -ComputerName Server01, Server02 -Verbose

Vstup do potrubí

Pokud chcete, aby funkce přijímala vstup kanálu, je potřeba další kód. Jak bylo zmíněno dříve v této knize, příkazy mohou přijímat vstup kanálu podle hodnoty (podle typu) nebo podle názvu vlastnosti. Funkce můžete psát jako nativní příkazy, aby přijímaly jeden nebo oba tyto typy vstupů.

Pokud chcete přijmout vstup kanálu jako hodnotu, zadejte atribut parametru ValueFromPipeline pro tento konkrétní parametr. Můžete přijímat vstup kanálu pouze podle hodnoty z jednoho parametru každého datového typu. Pokud máte dva parametry, které přijímají řetězcový vstup, pouze jeden z nich může přijímat kanálový vstup podle hodnoty. Pokud jste pro oba parametry řetězce zadali hodnotou, vstup by nevěděl, ke kterému parametru se má svázat. Tento scénář je dalším důvodem, proč volám tento typ vstupu kanálu podle typu místo podle hodnoty.

Vstup zpracovatelského potrubí je přijímán po jedné položce, podobně jako se položky zpracovávají v cyklu foreach. Blok process je nutný ke zpracování každé položky, pokud vaše funkce přijímá jako vstup pole. Pokud vaše funkce jako vstup přijímá jenom jednu hodnotu, není blok process nutný, ale doporučuje se pro konzistenci.

function Test-MrPipelineInput {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory,
                   ValueFromPipeline)]
        [string[]]$ComputerName
    )

    process {
        Write-Output $ComputerName
    }

}

Přijetí vstupu kanálu podle názvu vlastnosti je obdobné, až na to, že je nutné použít atribut parametru ValueFromPipelineByPropertyName a atribut může být specifikován pro libovolný počet parametrů bez ohledu na datový typ. Klíč je výstupem příkazu, který se předá, musí mít název vlastnosti, který odpovídá názvu parametru nebo aliasu parametru vaší funkce.

function Test-MrPipelineInput {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory,
                   ValueFromPipelineByPropertyName)]
        [string[]]$ComputerName
    )

    process {
            Write-Output $ComputerName
    }

}

begin a bloky end jsou volitelné. begin se zadává před blokem process a slouží k provedení jakékoli počáteční práce před přijetím položek z kanálu. Hodnoty, které jsou přesměrovány, nejsou v bloku begin přístupné. Blok end se zadává po bloku process a slouží k vyčištění po zpracování všech položek.

Zpracování chyb

Funkce zobrazená v následujícím příkladu vygeneruje neošetřenou výjimku, když počítač nelze kontaktovat.

function Test-MrErrorHandling {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory,
                   ValueFromPipeline,
                   ValueFromPipelineByPropertyName)]
        [string[]]$ComputerName
    )

    process {
        foreach ($Computer in $ComputerName) {
            Test-WSMan -ComputerName $Computer
        }
    }

}

Existuje několik různých způsobů zpracování chyb v PowerShellu. Try/Catch je modernější způsob zpracování chyb.

function Test-MrErrorHandling {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory,
                   ValueFromPipeline,
                   ValueFromPipelineByPropertyName)]
        [string[]]$ComputerName
    )

    process {
        foreach ($Computer in $ComputerName) {
            try {
                Test-WSMan -ComputerName $Computer
            }
            catch {
                Write-Warning -Message "Unable to connect to Computer: $Computer"
            }
        }
    }

}

I když funkce zobrazená v předchozím příkladu používá zpracování chyb, vygeneruje neošetřenou výjimku, protože příkaz negeneruje ukončující chybu. Zachyceny jsou pouze ukončující chyby. Zadejte parametr ErrorAction s hodnotou Zastavit, aby se z nekončící chyby stala chyba končící.

function Test-MrErrorHandling {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory,
                   ValueFromPipeline,
                   ValueFromPipelineByPropertyName)]
        [string[]]$ComputerName
    )

    process {
        foreach ($Computer in $ComputerName) {
            try {
                Test-WSMan -ComputerName $Computer -ErrorAction Stop
            }
            catch {
                Write-Warning -Message "Unable to connect to Computer: $Computer"
            }
        }
    }

}

Neupravujte globální $ErrorActionPreference proměnnou, pokud to není nezbytně nutné. Pokud ho změníte v místním oboru, při ukončení tohoto oboru se vrátí k předchozí hodnotě.

Pokud používáte něco jako .NET přímo z funkce PowerShellu, nemůžete v samotném příkazu zadat parametr Error Action. Proměnnou $ErrorActionPreference můžete změnit těsně před voláním metody .NET.

Nápověda založená na komentářích

Přidání nápovědy k funkcím se považuje za osvědčený postup. Pomoc umožňuje lidem, se kterými je sdílíte, vědět, jak je používat.

function Get-MrAutoStoppedService {

<#
.SYNOPSIS
    Returns a list of services that are set to start automatically, are not
    currently running, excluding the services that are set to delayed start.

.DESCRIPTION
    Get-MrAutoStoppedService is a function that returns a list of services
    from the specified remote computer(s) that are set to start
    automatically, are not currently running, and it excludes the services
    that are set to start automatically with a delayed startup.

.PARAMETER ComputerName
    The remote computer(s) to check the status of the services on.

.PARAMETER Credential
    Specifies a user account that has permission to perform this action. The
    default is the current user.

.EXAMPLE
     Get-MrAutoStoppedService -ComputerName 'Server1', 'Server2'

.EXAMPLE
     'Server1', 'Server2' | Get-MrAutoStoppedService

.EXAMPLE
     Get-MrAutoStoppedService -ComputerName 'Server1' -Credential (Get-Credential)

.INPUTS
    String

.OUTPUTS
    PSCustomObject

.NOTES
    Author:  Mike F. Robbins
    Website: https://mikefrobbins.com
    Twitter: @mikefrobbins
#>

    [CmdletBinding()]
    param (

    )

    #Function Body

}

Když do funkcí přidáte nápovědu založenou na komentářích, lze ji načíst stejně jako pomoc pro výchozí integrované příkazy.

Veškerá syntaxe pro psaní funkce v PowerShellu se může zdát ohromující pro začátečníka. Pokud si nepamatujete syntaxi něčeho, otevřete druhou instanci integrovaného skriptovacího prostředí PowerShellu (ISE) na samostatném monitoru a při psaní kódu pro vaše funkce si prohlédněte ukázku kódu "Cmdlet (pokročilá funkce) - Complete." Fragmenty kódu jsou přístupné v prostředí PowerShell ISE pomocí kombinace kláves Ctrl + J.

Shrnutí

V této kapitole jste se naučili základy psaní funkcí v PowerShellu, včetně postupu:

  • Vytváření pokročilých funkcí
  • Použijte ověření parametrů
  • Použití podrobného výstupu
  • Podpora vstupu kanálu
  • Řešení chyb
  • Vytvoření nápovědy založené na komentářích

Přezkoumání

  1. Jak v PowerShellu získáte seznam schválených sloves?
  2. Jak převést funkci PowerShellu na pokročilou funkci?
  3. Kdy byste měli přidat parametry WhatIf a Confirm do funkcí PowerShellu?
  4. Jak změníte neterminující chybu na ukončující chybu?
  5. Proč byste měli ke svým funkcím přidat nápovědu založenou na komentářích?

Odkazy