Part 9. PowerShell as a programming language (Functions, filters, scripts and modules)

26 July 2023 34 minutes
Author: Lady Liberty

PowerShell: Functions, Filters, Scripts and Modules

Functions, filters, scripts, and modules in PowerShell open a wide range of possibilities for developers and system administrators. These concepts are important components of the PowerShell language, allowing you to create efficient and reusable scripts, providing ease of data management and processing. In our rich guide to functions, filters, scripts, and modules in PowerShell, we’ll cover all aspects of these concepts. Functions allow you to group code and provide a convenient way to use it in different parts of your program. Filters make it possible to process data according to certain conditions, simplifying complex tasks. Scripts allow you to execute a sequence of commands in a convenient format.

However, special emphasis will be placed on modules in PowerShell – powerful tools that allow you to create independent and high-quality software components. Modules help organize your code, ensure code reusability, and allow you to create your own convenient feature packages. With our guide, you’ll learn about the different types of functions, creating and using filters, working with scripts, and configuring modules. We’ll provide code samples and hands-on exercises to help you understand these concepts and become more confident using PowerShell. Explore the full range of functions, filters, scripts, and modules in PowerShell and develop your programming skills with our comprehensive guide. Improve your scripts, provide fast and efficient operations, and perform convenient data management in a Windows environment.

Functions, filters, scripts and modules

So far, we’ve worked mostly with standard compiled cmdlets whose functionality we can’t change because their source code isn’t available in PowerShell. Functions and scripts are two other types of PowerShell commands that you can create and modify at will using the PowerShell language.

It is immediately worth noting that functions in PowerShell have some features compared to functions in traditional programming languages, because PowerShell is primarily a shell. In traditional languages, a function is usually analogous to an object method, and in PowerShell, a function is a command. Hence the difference in how functions are called and arguments passed to them.

Usually, functions in traditional languages return a single value of one type or another. The value of a PowerShell function can be an array of different objects, because each expression evaluated in the function places its result in the output stream.

In addition, functions in PowerShell are divided into several types according to their behavior inside the command pipeline.

Therefore, in order to competently work in the shell and write correct programs in PowerShell, it is necessary to consider issues related to the implementation of functions and scripts in this system.

Functions in PowerShell

Recall that a function in PowerShell is a block of code that has a name and is in memory until the end of the current shell session. If the function is defined without formal parameters, then for its definition it is enough to specify the keyword function, then the name of the function and a list of expressions enclosed in curly brackets that make up the body of the function. For example, let’s create a MuEips function:

PS С:\> function MyFunc {"Всем привет!")
PS С:\>
Для вызова этой функции нужно просто ввести ее имя:
PS С:\> MyFunc
Всем привет!
Отметим, что во многих языках программирования при вызове функции после ее
имени нужно указывать круглые скобки. В PowerShell этого делать нельзя, возникнет ошибка:
PS С:\> MyFunc()
строка:1 знак:8
+ MyFunc()
После ’’(’' ожидалось выражение.
+ Categoryinfo : ParserError: (:) [],
+ FullyQualifiedErrorld : ExpectedExpression

This is because the last command is interpreted by the shell as a call to the MyFunc function with the () argument. Parentheses in PowerShell indicate the result of evaluating an expression or an array, so the system expects the parentheses to contain an expression or enumerated array elements, but there is nothing. Therefore, an error occurs, because in order to correctly declare an empty array in PowerShell, you need to put the sign e before the brackets.

Functions can work with arguments that are passed to them at startup, and two ways of handling such arguments are supported: by means of the $args variable and by describing formal parameters.

Argument handling using the $args variable

A function in PowerShell has access to the arguments with which it was invoked, even if no formal parameters were specified when the function was defined. All actual function arguments are automatically stored in the $args variable. In other words, the $args variable contains an array whose elements are the parameters of the function specified when it is started. For example, let’s add the $args variable to our Murips function:

PS С:\> function MyFunc {"Привет, $args!"}

Since the $args variable is enclosed in double quotes, when the function is run, the value of this variable will be calculated (expanded) and the result will be inserted into the string.

Let’s call the function MyFunc with three parameters:

PS С:\> MyFunc Андреи Сергей Иван
Привет, Андрей Сергей Иван!

As you can see, the three parameters we specified (the three elements of the $args array) are placed in the output line and separated by spaces. You can change the character that separates array elements when substituting them into expanding strings by changing the value of the $OFS special variable:

PS С:\> function MyFunc {
» $OFS = ","
» "Привет, $args!"}
PS C:\> MyFunc Андреи Сергеи Иван
Привет, Андрей,Сергей,Иван!

Again, note that unlike traditional programming languages, functions in PowerShell are commands (they’re not object methods!), so their arguments are space-separated with no comma-separating brackets and no character strings quoted. To verify this, let’s run the Murips function in the usual way for other programming languages:

PS С:\> MyFunc("Андрей", "Сергей", "Иван")
Привет, System.Object[]!

Recall that arrays in PowerShell are specified by enumerating their elements, and parentheses surrounding an expression indicate that the expression is to be evaluated. Therefore, there is no error with such a function call, but in this case, the Murips function is not given three symbolic arguments, but one argument, which is an array of three elements!

Since $args is an array variable, inside the function you can access the individual arguments by their sequence number (remember that array element numbering starts at zero) and use the Count of the Length property to determine the total number of arguments passed to the function.

For example, let’s create the SumArgs function, which will report the number of its arguments and calculate their sum:

PS С:\> function SumArgs{"Количество аргументов: $($args.count)"
» $n = О
» for($i =0; $i -It $args.count; $i++) { $n += $args[$i] }
» "Сумма аргументов: $n")
PS C:\>
Вызовем функцию SumArgs с тремя числовыми аргументами:
PS C:\> SumArgs 123
Количество аргументов: 3
Сумма аргументов: 6

In addition to the $args array, PowerShell supports a more familiar and convenient approach to handling function arguments by setting formal parameters.

Formal parameters of functions

In PowerShell, as in most other programming languages, when describing a function, you can specify a list of formal parameters whose values will be replaced by the values of the actual arguments passed when the function is executed.

At the same time, the list of formal parameters can be specified in parentheses after the function name (this is how function parameters are described in most programming languages) or inside the function itself using the special Param operator.

Positional and named parameters

Let’s define, for example, the subtraction function to find the difference between its two arguments (the $from parameter corresponds to the decreasing parameter, the $count parameter corresponds to the subtracted parameter): PS C: \> functxon subtract ($frcm, $count) {$from – $cotmt}

When the subtraction function is called, its formal parameters are replaced by actual arguments, identified by position on the command line or by name.

PS С:\> subtract 10 2
В этом случае соответствие формальных параметров фактически переданным аргументам определяется по позиции: вместо первого параметра $from подставляется
число 10, вместо второго параметра $count подставляется число 2.
При указании аргументов можно использовать имена формальных параметров,
порядок указания аргументов при этом становится несущественным. Например:
PS С:\> subtract -from 10 -count 2
PS C:\> subtract -count 3 -from 5
Если дважды указать имя одного и того же параметра, то возникнет ошибка:
PS С:\> subtract -from 10 -from 2
subtract : He удается привязать параметр, т. к. параметр "from” указан более
одного раза. Для указания множественных значений параметров, допускающих множественные значения, используйте синтаксис массива.
Например, "-parameter valuel,value2,value3".
строка:1 знак:19
+ subtract -from 10 -from 2
+ —
+ Categoryinfo : InvalidArgument: (:) [subtract],
+ FullyQualifiedErrorld : ParameterAlreadyBound,subtract

When calling a function, a third option for specifying arguments is also possible, when some are given names, and some are determined by the position in the command line.

In this case, the following algorithm is used:

  • All named arguments are matched with the corresponding formal parameters and removed from the argument list;

  • The remaining parameters (nameless or with a name that does not correspond to any formal parameter) are mapped to formal parameters by positions.


PS С:\> subtract -from 10 2
PS С:\> subtract -count 2 10
По умолчанию функции PowerShell, как и другие команды оболочки, ведут себя
полиморфным образом, т. е. могут принимать аргументы разных типов. Например,
определим функцию add, складывающую два своих аргумента:
PS С:\> function add ($х, $у) {$х + $у}
Выполним эту функцию с аргументами-числами и аргументами-строками:
PS С:\> add 2 3
PS С:\> add "2" "3"
В первом случае функция add возвращает число 5, а во втором — строку ”23”:
PS С:\> (add 2 3).getType().fullName
PS C:\> (add "2" "3").getType().fullName
При вызове функции может быть указано большее количество фактических аргументов, чем было задано формальных параметров. При этом ’’лишние” аргументы
будут помещены В массив $args. Для иллюстрации рассмотрим функцию ShowArgs
с двумя формальными параметрами:
PS С:\> function ShowArgs ($х,$у) {
» "Первый аргумент: $х"
>> "Второй аргумент: $у"
>> "Остальные аргументы: $args"}
Вызовем эту функцию с одним аргументом:
PS С:\> ShowArgs 1
Первый аргумент: 1
Второй аргумент:
Остальные аргументы:
В этом случае единственный аргумент сопоставляется с параметром $х, значение
параметра $у становится равным $nuii, а массив $args не содержит элементов
($args. length=0).
Запустим теперь функцию ShowArgs с двумя аргументами:
PS С:\> ShowArgs 1 2
Первый аргумент: 1
Второй аргумент: 2
Остальные аргументы:
Как видим, в данном случае параметры $х и $у получают значения 1 и 2, а массив
$args остается пустым.
Наконец, запустим функцию ShowArgs с тремя и четырьмя аргументами:
PS С:\> ShowArgs 123
Первый аргумент: 1
Второй аргумент: 2
Остальные аргументы: 3
PS С:\> ShowArgs 1234
Первый аргумент: 1
Второй аргумент: 2
Остальные аргументы: 3 4
Дополнительные аргументы действительно сохраняются в массиве $args.

Restriction of parameters by types

When declaring a function, you can explicitly specify the type of formal parameters. For example, let’s define a function int add that will add two integer arguments:

PS С:\> function int_add ([int]$x, [int]$y) {$x + $y}
Как и в предыдущем случае, вызовем данную функцию с аргументами-числами и
с аргументами-строками:
PS С:\> int_add 2 3
PS С:\> int_add ”2" "3”
Как видим, результат получается один и тот же, символьные аргументы автоматически преобразовались к целочисленному типу.
Если преобразование выполнить не удается, то возникнет ошибка:
PS С:\> int_add "2а" "3"
int_add : Не удается обработать преобразование аргументов для параметра "х".
Не удается преобразовать значение "2а" в тип "System.Int32". Ошибка: "Входная
строка имела неверный формат."
строка:1 знак:9
+ int_add "2а" "3"
+ Categoryinfo : InvalidData: (:) [int_add],
+ FullyQualifiedErrorld : ParameterArgumentTransformationError,int_add

Default parameter values

When declaring formal parameters, you can specify the values that those parameters will take by default (unless you explicitly specify a corresponding actual argument).

Например, определим функцию add с двумя формальными параметрами $х и $у,
которые по умолчанию инициализируются числами 2 и 3:
PS С:\> function add ($х =2, $у = 3) {$х + $у}
Если запустить эту функцию без аргументов, то оба параметра инициализируются
значениями по умолчанию и функция возвращает число 5:
PS С:\> add
Если запустить функцию add с одним аргументом, то указанное значение присвоится параметру $х, а параметр $у вновь инициализируется значением по умолчанию
PS С:\> add 5
При указании двух параметров функция add вернет их сумму:
PS С:\> add б 6

Additional attributes and parameter validation

Before the parameter type and its name, you can specify an additional set of specifications in square brackets in the format:

parameter(ключ = значение [, . . . ] )

Thus, it is possible, for example, to indicate that the parameter is mandatory, define its short alias and specify the description of this parameter. In the table 9.1 shows some of these keys.

For example, let’s create a Print-Message function that will print the string passed to it as an argument multiple times. The parameter for the text of the displayed message will be called -message, the number of repetitions will be omitted via the -repeat parameter.

function Print-Message (
[parameter(Mandatory=$true, HelpMessage=’3To очень важный параметр!’)]
[int]$repeat = 1
for ($i =1; $i -le $repeat; $i++) {

Here, when defining the function, we indicated that the parameter of the $message symbol is mandatory (Mandatory key with the value $true), and set its short description (He1pMessage key). The integer parameter $repeat can not be specified when calling the function, its value is equal to one by default. If you run Print-Message without parameters, the shell will prompt you to specify the value of the required -message parameter:

PS С:\Users\andrv> Print-Message
Командлет Print-Message в конвейере команд в позиции 1
Укажите значения для следующих параметров:
(Для получения справки введите "!?’’)
Если вместо значения -message ввести символы ! ?, tq будет отображено описание
этого параметра:
PS С:\Users\andrv> Print-Message
Командлет Print-Message в конвейере команд в позиции 1
Укажите значения для следующих параметров:
(Для получения справки введите "!?”)
message: !?
Это очень важный параьЛётр!

Also, with the help of additional attributes, you can set rules for checking (validating) parameter values. Some of these attributes are listed in the table. 9.2.

Validation attributes are very useful when writing functions and scripts, they allow you to avoid writing repetitive template code to validate incoming documents. For example, to limit the number of messages displayed in the Print-Message function, you can add the -repeat attribute to the parameter description:

function Print-Message (
[parameter(Mandatory=$true, HelpMessage='Это очень важный
[ValidateRange(1, 3)][int]$repeat = 1
for ($i = 1; $i -le $repeat; $i++) {
Теперь при попытке передать в параметр -repeat число, большее трех, возникнет
PS С:\Users\andrv> Print-Message -message 'Привет всем!' -repeat 5
Print-Message : Не удается проверить аргумент для параметра "repeat".
Аргумент 5 больше максимально допустимого значения 3.
Укажите аргумент, значение которого меньше или равно 3, и повторите команду,
строка:1 знак:42
+ printmsg -message ’Привет всем! ' -repeat 5
-ь Categoryinfo : InvalidData: (:) [printmsg],
+ FullyQualifiedErrorld : ParameterArgumentValidationError,printmsg

For more information about parameter attributes, see the advanced_parameters function chapter of the PowerShell Help.

Switch settings

So far, we have considered the actual parameters of two types of functions: named (the name of a formal parameter and the value that this parameter should take) and positional (only the value of the argument is specified; the corresponding formal parameter is determined by the position of this argument on the command line).

Recall that PowerShell cmdlets support arguments of the third type, which are switchable parameters specified only by their own name. Such a parameter is, for example, the -Recurse recurse cmdlet in the GetChi1dIteme cmdlet. If this switch is specified, Get-Chi1dItem affects not only the current directory, but all its subdirectories.

You can also use similar switch parameters in functions that must be of type SwitchParameter, which is aliased [switchj . The values of the switch can only be $true or $false, so there is no need to initialize such a formal parameter.

For example, let’s create a Murips function with one switch parameter that defines the behavior of the function:

PS С:\> function MyFunc ([switch] $recurse) {
» if ($recurse) {
» "Рекурсивный вариант функции"
» }
» else { "Обычный вариант функции"
» }
» }
Запустив функцию MyFunc без параметра, мы получим сообщение, что она работает
в обычном режиме:
PS С:\> MyFunc
Обычный вариант функции
Если указан параметр -recurse, ТО функция MyFunc будет работать в рекурсивном
PS С:\> MyFunc -recurse
Рекурсивный вариант функции

Description of parameters in the Ragat() operator

All parameter descriptions, which we previously wrote in parentheses after the function name, can be placed inside the code of the function itself. For this, the first executed statement in the function code block should be the Param() statement and list inside it all the formal parameters of the function with the types, additional attributes and default values (the format is the same as we used before).

For example, the previously discussed Print-Message function using the Param ( ) operator would be written as follows:

function Print-Message ()
param (
[parameter(Mandatоry=$true, HelpMessage=’Это очень важный
[ValidateRange(1, 3)][int]$repeat = 1
for ($i = 1; $i -le $repeat; $i++) {

Passing parameters using variable partitioning

PowerShell supports a special way to pass values to cmdlet or function arguments using a pre-staged variable. This technique is called variable spraying.

The first version of the layout allows you to set the values of the positional parameters using a variable. For example, let’s create a Print-Args function that displays the values of its three arguments:

PS С: \Users\andrv> function Print-Args (param ($x, $y, $z) "x=$x, y=$yf z=$z" }
Выполним Print-Args, передав в качестве аргументов три числа:
PS С:\Users\andrv> Print-Args 123
х=1, у=2, z=3
Создадим теперь массив $а с теми же числами:
PS С:\Users\andrv> $а = 1, 2, 3
Передадим переменную $а В функцию Print-Args:
PS С:\Users\andrv> Print-Args $а
х=1 2 3, у=, z=

As you can see, all three elements of the $a array are assigned to the $x parameter inside the Print-Args function. Now let’s split the variable $a by specifying the name of the variable instead of the $ symbol when calling the Print-Agrs function:

Выполним теперь сплаттинг переменной $а, указав при вызове функции Print-Agrs
перед именем переменной вместо символа $ символ @:
PS С:\Users\andrv> Print-Args @а
х=1, у=2, z=3
В этом случае элементы массива $а соответствуют позиционным параметрам функции: $х = $а[0], $у = $а[1], $z = $а[2].
Второй вариант сплаттинга связан с передачей значений именованным аргументам
функции с помощью хэш-таблицы, ключи в которой совпадают с именами аргументов. Определим для нашего примера хэш-таблицу $ь:
PS С:\Users\andrv> $b = @{х=4; у=5; z=6}
Вызовем функцию Print-Args с результатом сплаттинга переменной $ь:
PS С:\Users\andrv> Print-Args @b
х=4, у=5, z=6
Как видим, значения их хэш-таблицы $ь попали в соответствующие параметры
функции: $х = $Ь[ ’х’ ], $у = $Ь[ 'у' ], $Z = $b[ 'z' ].

Returned values

In traditional programming, a function usually returns a single value of a specific type. This is not the case in PowerShell, where the results of all expressions or pipelines evaluated inside a function are routed to the output stream. Consider, for example, the MuEips function, which evaluates three numeric expressions:

В традиционных языках программирования функция обычно возвращает единственное значение определенного типа. В оболочке PowerShell дело обстоит иначе —
здесь результаты всех выражений или конвейеров, вычисляемые внутри функции,
направляются в выходной поток. Рассмотрим, например, функцию MyFunc, в которой вычисляются три числовых выражения:
PS р:\> function MyFunc {1+2; 3*3; 12/4}
Как видим, в этой функции явно не возвращается ни одно значение. Запустим
PS С:\> MyFunc
На экран выводятся три строки с результатами вычислений. Теперь запустим данную функцию и сохраним результат ее работы в переменной $resuit:
PS С:\> $result = MyFunc
Проверим тип переменной $ result:
PS С:\> $result.getType().fullName
Переменная $ result является массивом объектов. Найдем длину этого массива
и значения его элементов:
PS С:\> $result.Length
PS С:\> $result[0]
PS C:\> $result[l]
PS C:\> $result[2]

So, the $result array contains all the values that got into the standard output stream and were displayed on the screen during the MuEips function. This fact must be taken into account when writing and debugging functions. For example, let’s define a Murips function of the following form:

PS С:\> function MyFunc {
» $name = Read-Host "Введите свое имя"
» "Здравствуйте, $name!"
» $name
» }

This function prompts for the username (keyboard input is provided in the $path variable using the Read-Host cmdlet), displays a greeting for that user, and prints the value of the $path variable to the output stream. Let’s run the MuEips function:

PS С:\> MyFunc
Введите свое имя: Андрей
Здравствуйте, Андрей!
Проверим теперь, какие данные возвращает функция:
PS С:\> $result = MyFunc
Введите свое имя: Андрей
PS С:\> $result.length
PS С:\> $result
Здравствуйте, Андрей!

Yes, in addition to the value of the $path variable, a greeting string was also included in the output stream. However, if you use the Write-Host cmdlet to display a character string instead of a regular double-quoted string, the string will not be added to the output stream:

PS С:\> function MyFunc {
» $naxne — Read-Host "Введите свое имя"
>> Write-Host "Здравствуйте, $naxne!"
» $name
» }
PS C:\> $resuit = MyFunc
Введите свое имя: Андрей
Здравствуйте, Андрей!
PS С:\> $result.length
PS С:\> $result

The Write-Host cmdlet prints a string directly to the screen, not to the output stream, so in this case the Mugyps function returns a single string, the value of the $path variable. PowerShell has a Return statement that immediately exits the function (similar to the Break statement described in Chapter 8 for exiting a loop). Example:

PS С:\> function MyFunc {
» "Эта строка выводится на экран"
» return
» "Эта строка никогда не будет напечатана"
» }
PS С:\> MyFunc
Эта строка выводится на экран
PS С:\>
С помощью данной инструкции можно ’’возвратить" значение из функции, указав
нужный объект после слова return. Например, return 10*3 ИЛИ return "Возвращаемая строка".

Functions inside the command pipeline

The entire ideology of PowerShell is built on the use of command pipelines, and functions are no exception: they can also be used inside pipelines. At the same time, the $input variable is used to receive an input stream of objects passed from another command inside the function. This variable will contain a set of input objects.

Let’s consider an example:

PS С:\> function sum {
» $n=0
» foreach ($i in $input) { $n+=$I }
» $n
» }
PS C:\>

Here we have defined a sum function that will sum up the elements of the collection that came to it through the pipeline. The result of the summation is placed in the output stream as the value of the $p variable. After passing the array of function numbers through the pipeline, we will get their sum at the output:

PS С:\> 1..10 | sum

You can iterate through the elements of the input stream not only using the Forach loop, but also by calling the MoveNext ( ) method, while addressing the current element, you can use the Current property of the input stream. The summation function of the elements of the input stream can be rewritten as follows:

PS С:\> function sum2 {
» $n=0
» while ($input.moveNext ()) {
>> $n+=$input.current
» }
» $n
» }
Запустим функцию sum2 и убедимся, что она выдает тот же результат, что и функция sum:
PS С:\> 1..10 | sum2

Thus, it is not difficult to write a function to work inside the pipeline (it is enough to know about the assignment of the $input variable), but when receiving data from the previous command, the function pauses the pipeline and starts only once, when the entire collection of input elements is formed.

Cmdlet functions. Enhanced capabilities

The functions we worked with in the previous section did not fully support the pipelining mechanism, unlike compiled cmdlets that process an input pipeline element without waiting for subsequent elements to be generated. In addition, cmdlets have built-in help and support general options. Similar behavior can be implemented for PowerShell functions.

Three phases of functional work in the pipeline

When developing compiled PowerShell cmdlets, you can specify three types of actions:

  1. Executed before processing the first input element.

  2. Executed for each object in the input stream.

  3. Executed after the last item that came through the pipeline has been processed.

PowerShell provides the ability to implement this behavior for custom functions that have three sections: BEGIN, PROCESS, and END. In these functions, you can initialize some state before starting to work with pipeline elements, process each input object (without waiting for subsequent elements to be generated), and perform certain finishing actions after processing the last element of the pipeline. The general syntax of such functions is as follows:

function имя_функции ( параметры ) {

The BEGIN keyword specifies the section from which commands will be executed before processing the first pipeline object. Commands and expressions from the PROCESS section will be executed each time a new object arrives on the pipeline (access to the current item in this section is via the $_ variable). The END section contains code that will be executed after processing the last object in the pipeline. Let’s look at an example.

Let’s define the function MyCmd1et with all three sections:

PS С:\> function MyCmdiet ($а) {
» $n = 0; "Инициализация: n=$n, а=$а"}
» $n++
» "Обработка конвейера: n=$n, а=$а, текущий обгьект = $_" }
» END {"Завершение: п=$п, а=$а"}
» }

In each of the sections, information about the value of two variables is displayed: the formal parameter $a corresponds to the argument passed to the function, the variable $p is defined in the initialization section and is successively increased in the processing section. In addition, the processing section displays the value of the current object received via the pipeline (variable $_). After running this function with an argument (the number 4), we pass the numbers 5 to 8 to it via the pipeline:

PS С:\> 5..8 | MyCmdiet 4
Инициализация: п=0, а=4
Обработка конвейера: п=1, а=4, текущий объект = 5
Обработка конвейера: п=2, а=4, текущий объект = 6
Обработка конвейера: п=3, а=4, текущий объект = 7
Обработка конвейера: п=4, а=4, текущий объект = 8
Завершение?: п=4, а=4

As you can see, the function argument (number 4) and the $p variable created in the initialization section are available in all three sections. Now let’s run the MyCmdlet function without the pipeline:

PS С:\> MyCmdiet 4
Инициализация: п=0, а=4
Обработка конвейера: п=1, а=4, текущий объект =
Завершение: п=1, а=4

From this, we conclude that commands from the PROCESS section are executed once even in the absence of a pipeline.

Access to shared options and additional streams. Enhanced capabilities

Recall that cmdlets in PowerShell support a number of common parameters (-Verbose, -Debug, -WhatIf, and so on). These parameters can also be processed inside the so-called extended functions, which inside their body before the Param() operator contain some attributes (metadata) that affect the behavior of the function.

To enable support for generic function parameters, you must add the [Cmd1etBinding ( )) attribute to the function to make the function work as a cmdlet. Let’s look at a simple example of how this attribute affects the behavior of a function. Let’s start with the Sum-TwoArgs function, which sums the values of its two arguments:

PS С:\Users\andrv> function Sum-TwoArgs {
» param ($a, $b)
» $a + $b
» }
Проверим синтаксис этой функции с помощью параметра -syntax команды GetCommand:
PS С:\Users\andrv> Get-Command Sum-TwoArgs -Syntax
Sum-TwoArgs [ [—a] <Object>] [[—b] <Object>]
Как видим, Sum-TwoArgs не поддерживает общие параметры командлетов.
Вызовем нашу функцию с тремя параметрами:
PS С:\Users\andrv> Sum-TwoArgs 123
В этом случае ошибки не произошло, была вычислена сумма первых двух аргументов, а третий аргумент был помещен в переменную Sargs.
Добавим теперь к функции Sum-TwoArgs перед оператором Paramo атрибут
PS С:\Users\andrv> function Sum-TwoArgs {
» [CmdietBindingO]
190__________________________________ Часть II. PowerShell как язык программирования
» param ($а, $Ь)
» $а + $Ь
» }
Вновь проверим синтаксис:
PS С:\Users\andrv> Get-Command Sum-TwoArgs -Syntax
Sum-TwoArgs [[-a] <Object>] [[—b] <Object>] [<CommonParameters>]
Как видим, теперь функция Sum-TwoArgs поддерживает общие параметры.
Вновь вызовем функцию с тремя аргументами:
PS С:\Users\andrv> Sum-TwoArgs 123
Sum-TwoArgs : Не удается найти позиционный параметр, принимающий аргумент ”3”.
строка:1 знак:1
+ Sum-TwoArgs 123
+ Categoryinfo : InvalidArgument: (:) [Sum-TwoArgs],
+ FullyQualifiedErrorld : PositionalParameterNotFound,Sum-TwoArgs

Now we get an error message, because when calling cmdlets, the number of passed arguments should not exceed the number of formal parameters. So now our function behaves like a cmdlet.

A function with the [Cmd1etBinding( )] attribute can use Write-* commands to write messages to additional threads, such as Verbose or Debug. For example, let’s print a detailed message about the operation performed in the Verbose thread:

PS С:\Users\andrv> function Sum-TwoArgs {
» [ CmdletBinding () ]
» param (
» $a, $b
» )
» Write-Verbose "Функция вычисляет сумму двух аргументов: а=$а и Ь=$Ь"
» $а + $Ь
» }
При обычном запуске функции Sum-TwoArgs сообщения на экране мы не увидим:
PS С:\Users\andrv> Sum-TwoArgs 1 2
Выполним теперь функцию с общим параметром -verbose. В этом случае содержимое канала verbose будет отображено на экране.
PS С:\Users\andrv> Sum-TwoArgs 1 2 -Verbose
ПОДРОБНО: Функция вычисляет сумму двух аргументов: а=1 и Ь=2

PowerShell scripts

Until now, we have worked interactively in PowerShell, typed commands and received the result of their execution. If we need to execute the same commands tomorrow as we did today, we will have to type them again. Therefore, it is better to store frequently used command sequences in external scripts that are executed in batch mode, that is, without human intervention.

PowerShell scripts are text files with the PSL extension that contain code (commands, statements, and other constructs) in the PowerShell language. You can write PowerShell scripts step by step, directly in the shell itself, and then transfer the finished code to an external text file. This approach greatly simplifies learning the language and debugging scripts, allowing you to see the result of the execution of individual parts of the script at a glance.

Creating a scenario

There are many ways to create a PowerShell script file.

About Use an external text editor (such as standard Windows Notepad or Visual Studio Code stand-alone code) to manually enter the required commands and save the file with the PSL extension.

  • Run the desired commands in PowerShell, then copy them from the console to the Windows clipboard and paste them into a text file opened in an external text editor. The resulting file is saved with the extension psl.

  • If you are working in PowerShell, use the StartTranscript cmdlet to enable the command logging mode described in Section 4. This will create an external file with all the commands executed in the session. From this file, you can copy the commands you want into another text file and save it with a .psl extension.

  • While in PowerShell, format the PowerShell commands as strings (regular or here strings) and redirect those strings using the > characters and >> those strings to an external file with a .ps extension.

We will use the last of the proposed options and create a simple test.psl script in the script directory, which will consist of one line:

PS С:\Users\andrv\script> Set-ExecutionPolicy -Scope Process RemoteSigned
Итак, мы разрешили выполнение сценариев. Вновь попробуем запустить наш
скрипт, указав его имя:
PS С:\Users\andrv\script> test.psi
test.psi : Имя "test.psi" не распознано как имя командлета, функции, файла
сценария или выполняемой программы. Проверьте правильность написания имени,
а также наличие и правильность пути, после чего повторите попытку.
строка:1 знак:1
+ test.psi
+ ----
+ Categoryinfo : ObjectNotFound: (test.psi:String) [],
+ FullyQualifiedErrorld : CommandNotFoundException
Гпава 9. Функции, фильтры, сценарии и модули___________________________________ 193
Suggestion [3,General]: Команда test.psi не найдена, однако существует
в текущем расположении. По умолчанию оболочка Windows PowerShell не загружает
команды из текущего расположения. Если вы уверены в надежности команды, введите
".\test.psi”. Для получения дополнительных сведений вызовите справку с помощью
команды "get-help about_Cornmand_Precedence".

Error again … The fact is that when running PowerShell scripts, the path to the code file must always be specified explicitly, even if the script is located in the current directory, because this prevents the possible unauthorized execution of another executable program with the same name located, for example, in the system directory. Therefore, let’s run the script, explicitly specifying the path to it (the point in the path to Fight corresponds to the current directory):

PS С:\Users\andrv\script> .\test.psl
Эта строка печатается из сценария PowerShell
Итак, в этом случае сценарий выполняется. Можно даже не указывать расширение psi:
PS C:\Script> .\test
Эта строка печатается из сценария PowerShell
Естественно, путь можно было задать полностью:
PS C:\Script> C:\Users\andrv\script\test.psl
Эта строка печатается из сценария PowerShell
PS C:\Script> C:\Users\andrv\script\test
Эта строка печатается из сценария PowerShell
Если в пути к сценарию содержатся пробелы, то имя нужно заключить в кавычки,
и поставить в начале знак амперсанда (&), означающий в PowerShell оператор запуска. Например:
PS C:\Script> &'С:\Мои cxpnnTbi\script.psi'

Running a script from an external program

PowerShell scripts can be run directly from the cmd.exe command line or by using the Run option in the Start menu. To do this, you specify the full path to this script as the value of the -Fi1e option in the powershell.exe tool. In this case, the full path to powershell.exe and the exe extension can be omitted, for example:

С:\> powershell -File C:\Users\andrv\script\test.psl

We remind you that cmd.exe interpreter command files, as well as WSH scripts in VBScript or JScript are considered by Windows as executable, they can be launched from the browser by simply clicking on the icons of these scripts. This method does not work with PowerShell scripts – double-clicking the PowerShell script icon will not run it, but will open it for editing in Notepad (this is the correct approach from a security point of view to prevent the script from accidentally running).

Passing arguments in a script

Basically, a PowerShell script is a function that is not in RAM, but on external media. Therefore, the parsing and processing of arguments passed in the script is performed in approximately the same way as in functions.

Arguments are specified after the script name and are separated by spaces. The $args variable inside the script contains an array whose elements are the arguments of the function specified when it is started. For example, let’s write the SumArgs.ps1 script, which will report the number of parameters with which it is launched and their sum. Let’s create a source code file as follows: the text of the script will be formatted as a string of the here-string type (similar strings were described in chapter 7) and we will redirect this string to the SumArgs.psl file:

PS C:\Script> 0-'
» "Количество аргументов: $($args.count)"
» $n=0
» for($i=0; $i -It $args.count; $i++) { $n+=$args[$i] }
» "Сумма аргументов: $n"
» > SumArgs.psl
Запустим теперь сценарий SumArgs.psl с несколькими числовыми параметрами:
PS C:\Script> .\SumArgs 123
Количество аргументов: 3
Сумма аргументов: 6

As you can see, the $args array in scripts has the same meaning and is handled in the same way as in functions.

In scripts, you can define formal parameters that will be replaced at runtime by the actual arguments passed to the script. Recall that in functions we could list formal parameters either in parentheses after the name, that is, outside the body of the function, or inside the function using the Param () operator.

In scripts, parameters can be described only with the help of Param(), since here the entire content of the file is the body of the script. This statement must be the first command in the file to be executed, preceded only by blank lines, comments, and optional attributes in the case of extended scripts.

For example, let’s write the add.psl script with two formal parameters, which will output the sum of its arguments. To create a script, we will again use a string of the here-string type, which we will redirect to a file:

PS C:\Script> 0'
» param ($х=2, $у=3)
» $х+$у
» '@ > add.psi
Запустим полученный сценарий с аргументами и без них:
PS C:\Script> .\add 10 20
PS C:\Script> .\add
Все работает правильно: если аргументы не указаны, то внутри сценария используются значения по умолчанию

Exit from scripts. Return code

In normal mode, the exit from the script, as well as from the function, occurs after the execution of the last statement in it. Recall that the Return operator could be used in the function to force shutdown. The analogue of this operator for scripts is the Exit operator, which allows you to exit the script and return a certain return code if necessary. The return code can be analyzed in external programs (such as batch files or WSH scripts) that run the PowerShell script.

Let’s look at an example. Let’s create a Call PS.bat batch file that will call the PowerShell script specified by the line ” ‘ PoSH-script is working’; exit 10″ and display the value of the ERRORLEVEL environment variable equal to the return code of the last running process, as shown in Listing 9.1

Code listing 9.1. PowerSheIl script exit code definition (PS.bat call file)

@echo off
echo Запускаем сценарий PowerShell...
powershell "'PoSH-script is working'; exit 10"
echo Код возврата процесса PowerShell: %ERRORLEVEL%
Запустив файл Call_PS.bat в оболочке cmd.exe, мы получим следующий результат:
Запускаем сценарий PowerShell...
PoSH-script is working
Код возврата процесса PowerShell: 10

Areas of functions

A list of all the functions available in PowerShell is stored in the Function: virtual drive, and you can view its contents using the Get-Chi1dItem command (alias rer):

PS С:\Users\andrv> dir function:
CommandType Name
Function С:
cd. .

If we manually entered a function in the PowerShell window (or copied it from the clipboard), then it will be available only in the current shell session. In another PowerShell window, we won’t be able to use this feature.

Functions described in an external script file will only be available in that file. There are two ways to ensure that functions declared in a script remain in memory after the script is executed.

Dot-Source operator

If you run a PowerShell script in what’s called dot-search mode, where the path to the script is preceded by a period and a space, its contents will be executed as if they were entered directly on the command line. In other words, the script is executed in the command prompt window area, so all functions and variables present in the script become global and will be available in the shell after the script is finished.

To test this mode, let’s add a new function Loca1Func to the test.psl script with a default area:

PS С:\Users\andrv> "function LocalFuncO { 'Это функция LocalFunc' }"
» test.psi
Выполним сценарий test.psl в режиме dot-sourcing и убедимся в доступности функции LocalFunc В оболочке:
PS С:\Users\andrv> . .\test.psl
PS С:\Users\andrv> LocalFunc
Это функция LocalFunc
Подобным образом можно применить изменения в своем профиле, сделанные во
время текущего сеанса работы, без перезагрузки оболочки. Путь к профилю хранится в переменной $ profile, поэтому для этого достаточно выполнить следующую
PS С:\Users\andrv> . $PROFILE
В режиме dot-sourcing можно запускать не только сценарии, но и функции. Например:
PS С:\Users\andrv> function set-var ($х) {$х = $х}
PS С:\Users\andrv> . set-var 5
PS С:\Users\andrv> $х
Если некоторые переменные и функции из сценария должны остаться локальными
даже в режиме dot-sourcing, то перед их именами нужно указать спецификаторы
local: ИЛИ script:.

PowerShell modules

A great virtue of programmers and administrators is the reluctance to do unnecessary work. There is no need to reinvent the wheel again and again – if someone has already completely or partially solved a problem of interest to us, written the code for it, then it will be easier and more correct to use this solution, and not to write the code again.

Modern search engines on the Internet make life easier for developers, we can quickly find relevant examples in different programming languages on special sites. There is a centralized Microsoft-maintained repository for PowerShell scripts, the PowerShell Gallery (, which contains several thousand PowerShell language packs developed by Microsoft and other members of the PowerShell community. You can also find thousands on GitHub (the official PowerShell command repository is at

As soon as the script is found, you need to use it for your own purposes, and this is where the problem of simple and convenient code reuse arises. For this, in PowerShell, it is customary to form code in modules that contain module elements: cmdlets, functions, variables, aliases, which, if necessary, can be loaded into a shell and used from the command line or your scripts.

Modules in PowerShell help you accomplish two main tasks.

  • Add new functionality to the command shell. In previous chapters, we covered the procedure for loading your functions into a shell in dot-sourcing mode. Modules allow you to do this in a more convenient and reliable way.

  • Code reuse. Modules in PowerShell, as in other programming languages, allow you to create and distribute libraries from which functions are connected and used in their scripts.

Script modules

PowerShell uses several types of modules, including binaries, which contain compiled cmdlets, and Script modules, which are PowerShell scripts in files with a .psml extension (recall that regular PowerShell scripts have a .ps 1 extension).

If a module is in a subdirectory of one of the directories specified in the PSModu1ePath environment variable, it will be automatically loaded into the shell the first time any command from that module is called:

PS С: \Users\andrv> $ENV:PSModuiePath -split
С:\Program Files\WindowsPowerShell\Modules

Thus, script modules allow you to write your own functions that will work similarly to built-in cmdlets, being automatically loaded into the shell each time PowerShell is started.

For example, let’s write two functions, Out-Hello and Out-GoodBye, in the file MyModule.psml. These functions will output a message with the name of the current user:

PS С:\Users\andrv> 'function Out-Hello() { "Привет, $env: UserName! " }' >
MyModule. psml
PS C:\Users\andrv> 'function Out-GoodBye() { "Пока, $env:UserName! " }' »
MyModule. psml
Проверим содержимое файла MyModule.psml:
PS C:\Users\andrv> Get-Content . \MyModule.psml
function Out-Hello() { "Привет, $env:UserName!" }
function Out-GoodBye() { "Пока, $env:UserName!" }
В переменную $moduieDir сохраним каталог для хранения пользовательских модулей (первый каталог из переменной окружения PSModuiePath):
PS С:\Users\andrv> $moduleDir = ($ENV:PSModuiePath -split ';')[0]
Создадим в этом каталоге подкаталог MyModule (имя подкаталога должно совпадать с именем файла с модулем):
PS С: \Users\andrv> New-Item -Path $moduleDir/MyModule -ItemType Directory
Каталог: C:\Users\andrv\OneDrive\Documents\WindowsPowerShell\Modules
Mode LastWriteTime Length Name
d---- 11.06.2021 6:06 MyModule
Скопируем в созданный подкаталог файл MyModule.psml нашего модуля:
PS С:\Users\andrv> Copy-Item -Path .\MyModule.psml -Destination
Теперь наш модуль стал доступным для загрузки. Проверить это можно с помощью
командлета Get-Module С параметром -ListAvailable:
PS С: \Users\andrv> Get-Module -ListAvailable MyModule | Format-List
: MyModule
: C:\Users\andrv\OneDrive\Documents\WindowsPowerShell\
: Script
: 0.0
: {}
ExportedFunctions : {Out-Hello, Out-GoodBye}
ExportedCmdlets :
ExportedVariables :
ExportedAliases :
Как видим, оболочка нашла наш модуль и определила, что из него экспортируются
функции Out-Hello И Out-GoodBye.
По умолчанию из модуля экспортируются все его члены (функции, переменные, псевдонимы и т. д.). Это поведение можно изменить, явно перечислив экспортируемые
члены с помощью командлета Export-ModuleMember.
Загрузим наш модуль С ПОМОЩЬЮ командлета Import-Module. Параметр -Verbose
позволяет увидеть, какие фунции импортируются из модуля:
PS С: \Users\andrv> Inport-Module MyModule -Verbose
ПОДРОБНО: Импорт функции "Out-GoodBye".
ПОДРОБНО: Импорт функции "Out-Hello".
После импорта модуля MyModule В оболочке станут доступны функции Out-Hello
И Out-GoodBye:
PS С:\Users\andrv> Out-Hello
Привет, andrv!
PS C:\Users\andrv> Out-GoodBye
Пока, andrv!
При следующем запуске PowerShell модуль MyModule будет загружен автоматически, объявленные в нем функции останутся доступными.

PowerShell Gallery script repository

By default, the centralized repository for PowerShell modules and scripts is the PowerShell Gallery ( managed by Microsoft. Information about this repository can be obtained using the Get-PSRepository cmdlet:

PS С:\Users\andrv> Get-PSRepository | Format-List
PackageManagementProvider : NuGet
: PSGallery
: False
: True
: Untrusted
Как видим, централизованный репозиторий имеет имя PSGallery.
: {}
Работать с репозиторием можно с помощью команд из модуля PowerShellGet:
PS С:\Users\andrv> Get-Command -Module PowerShellGet | Format-Wide -Column 2

Finding modules is done using the Find-Module cmdlet. If you execute this command without parameters, a complete list of all modules available in the PowerShell Gallery will be displayed:

PS С: \Users\andrv> Find-Module -Repository PSGallery | Format-Wide -Column 2
Compute rManagement Dsc
Az .Network
Az .Websites

If the name of the desired module is known, it should be specified as the value of the -Name parameter. Example:

PS С:\Users\andrv> Find-Module -Repository PSGallery -Name PSLogging |
: PSLogging
: 2.5.2
: Module
: Creates and manages log files for your scripts.
: LucaSturlese
: 9to5lT
: (c) 2015 Luca Sturlese. All rights reserved.
: 11/22/2015 10:26:55 AM
: (Logging, LogFiles, PSModule}
: (Function, RoleCapability, Command, DscResource...
ReleaseNotes : Removed HelpInfoURI from Module Manifest file as
was causing an issue with PowerShell 2.0
: (}
: PSGallery
: NuGet

The module is installed from the repository using the Install-Module cmdlet. for modules are installed in the directory C:\Program Files\WindowsPowerShell\ Modules, which requires executing the Install-Module command in a shell running as an administrator. To install the module in the profile of the current user, you need to specify the -scope parameter with the value Currentuser and answer the installation question in the affirmative:

PS С:\Users\andrv> Install-Module -Name PSLogging -Scope Currentuser
Untrusted repository
You are installing the modules from an untrusted repository. If you trust this
repository, change its Installationpolicy value by running the Set-PSRepository
cmdlet. Are you sure you want to install the modules from ’PSGallery’?
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default
is "N") : у
Для проверки загруженного модуля можно посмотреть, какие команды в нем содержатся:
PS С:\Users\andrv> Get-Command -Module PSLogging
CommandType Name Version
204 Часть II. PowerShell как язык программирования
Обновить модуль (т. e. установить его новую версию) можно с помощью командлета Update-Module, удалить МОДУЛЬ С локальной машины— С ПОМОЩЬЮ UninstallModule.


  • Functions in PowerShell are created using the function keyword.

  • Unlike other programming languages, a function in PowerShell is a command, so you need to call functions as commands—arguments are separated by a space after the function name.

  • The easiest way to pass arguments to a function is to iterate over the elements of the $args array.

  • You can specify formal parameters for a function in PowerShell by specifying them in parentheses after the function name (as is done in other programming languages) or inside the function itself using the special Param() operator. At the same time, you can explicitly specify the types of such parameters, set their default values and other attributes to check the arguments passed to the function when called.

  • When a function is called, its formal parameters are replaced by actual arguments, which are identified by position on the command line or parameter name.

  • The body of a function can contain BEGIN, PROCESS, and END sections. In this case, functions can be properly used inside pipelines.

  • If you set the Cmd1etBinding ( ) attribute to a function, it will behave like a cmdlet. Such functions are called advanced, they make available general cmdlet parameters and additional output streams Verbose, Debug, Error.

  • Each expression evaluated in the function places its result in the output stream.

  • To control the features available in the current PowerShell session, use Feature: Virtual Disk.

  • A PowerShell script is a function that does not reside in RAM, but is stored in a text file with the extension .psl. The definition, parsing, and processing of arguments passed in a script are performed in the same way as in functions.

  • The execution mode of scripts in PowerShell is determined by the execution policy, which can be changed using the Set-ExecutionP01icy command. I recommend-

  • Functions in PowerShell can have different scopes. To ensure that the functions declared in the script remain available in the shell after the script is executed, you can create the functions globally or run the script in dot-sourcing mode.

  • By default, a variable in PowerShell is local to the script, function, or code block in which it was initialized. Variable scopes can be set explicitly by specifying qualifiers before their names.

  • To facilitate code reuse, PowerShell modules are used, which contain cmdlets, functions, variables, and aliases that can be optionally loaded into a shell and used from the command line or from scripts.

  • Microsoft maintains a centralized repository of PowerShell modules and scripts, the PowerShell Gallery.

Thanks to our team of volunteers for providing information from open sources.

Found an error?
If you find an error, take a screenshot and send it to the bot.