Во всех языках программирования так же как и в Powershell переменные несут ключевой характер. Переменные нужны для хранения, вывода и передачи значений. Существует множество типов переменных, каждый из которых мы разберем ниже. В этой статье будут разобраны примеры по выводу, созданию, удалению переменных и многие другие действия.
Создание
Переменная - это ссылка на значение, которое хранится в памяти. Каждая переменная в Powershell начинается со знака доллара "$" и может хранить числа, буквы и нижние подчеркивания. Присваивание значений осуществляется через знак равно "=". На примере ниже мы создали переменную variable со значением 8:
$variables = 8
Что бы вывести переменную в консоли ее нужно повторно написать:
$variables
Мы можем проводить арифметические операции с разными переменными:
$variable1 = 8
$variable2 = 7
$variable1 - $variable2
Такие операции можно проводить и со строками. На примере ниже мы выполняем сложение:
$variable1 = "один два"
$variable2 = " три"
$variable1 + $variable2
Если выполнять сложение числа и строки, то число автоматически преобразуется в строку:
$str= "один два "
$int = 3
$str + $int
Такое поведение одинаково не во всех языках и таких ситуаций лучше избегать.
Перезаписать значение переменной можно так:
$variables = 1
$variables = 2
$variables
Переменные живут в рамках текущего сеанса. Если вы работаете через консоль - после ее закрытия они удаляться, если это работающий скрипт - до закрытия программы. Исключения только с системными переменными и импортируемыми модулями. Одна из таких переменных, которая создается каждый сеанс $PSVersionTable, хранящая версию Powershell и ее не получится изменить или создать заново.
Типы данных
Кроме строк и чисел в Powershell существуют и другие типы. Самые популярные из них:
- [string] - строка:
- [char] - код символа ASCII
- [bool] - булево значение "True","False";
- [int] - число длинною в 32 бита;
- [long] - число длинною в 64 бита;
- [decimal] - число с плавающей точкой диною в 128 бит и d на конце;
- [double] - 8 битовое число с плавающей точкой;
- [single] - 32 битовое число с плавающей точкой;
- [DateTime] - тип данных Powershell хранящий дату и время;
- [array] - массив;
- [hashtable] - хэш таблица;
- [pscustomonject] - массив типа ключ и значение.
В большинстве случаев нет необходимости объявлять тип переменной, так как Powershell определяет это сам. В некоторых языках объявление типов переменных ускоряет работу программ.
Если вы сомневаетесь в типе переменной можно использовать GetType():
$a = 1
$a.GetType()
$b = @('Array', 'Array2')
$b.GetType()
Назначать типы можно как значению, так и самой переменной:
[int]$num = 1
$num
$num1 = [int]1
$num1
Если мы попробуем преобразовать строку в число указывая ее тип, то получим ошибку так как невозможно сделать из букв числа:
[int]$num = 'Test'
$num
Есть некоторые типы, которые объявлять обязательно, так как Powershell может конвертировать в другой формат. Один из таких примеров PSCustomObject. Если мы не объявим этот тип, то получим hashtable:
$data = [PSCustomObject]@{Name='Alex'}
$data.GetType()
$data1 = @{'Name'='Alex'}
$data1.GetType()
Про работу с такими типами вы можете почитать в предыдущих статьях, например работа с хеш таблицами в Powershell Hashtable.
Дату так же можно объявлять несколькими путями. Это можно делать через команду и объявляя тип (используя свой стандарт времени):
$date = Get-Date
$date
[datetime]$date1 = '2019-11-26'
$date
Кавычки и форматирование строк
Вы можете использовать одинарные и двойные кавычки. Основное различие в том, что двойные кавычки позволяют передавать переменные внутри строки:
$ch = 1
$sd = "Lesson number $ch"
$sd
$ch = 1
$sd = 'Lesson number $ch'
$sd
Конечно можно передавать данные используя параметр форматирования:
$ch = 1
$sd = 'Lesson number {0}' -f $ch
$sd
Еще один способ с методом .NET, который работает так же как предыдущий:
$ch = 1
$sd = [string]::Format('Lesson number {0}', $ch)
$sd
Примерно так же форматирование используется и с массивами. Отличия в том, что свойства не могут быть вызваны, если переменная в строке с использованием двойных кавычек:
$hash = @{FName = 'Alex';}
$insert = "He is {0}" -f $hash.FName
$insert
$hash = @{FName = 'Alex';}
$insert = "He is $hash.FName"
$insert
Системные $_ и $PSItem и конвейер
Когда мы работаем с существующими командами Powershell бывает необходимо передать определенное значение в параметр. На примере ниже у нас есть список сервисов статус которых мы хотим получить:
$massive = @('WinRM','NetLogon')
$massive | Get-Service
На самом деле команда сама подставляет нужные переменные под нужные параметры, которые в целом выглядят так:
$massive | Get-Service -PipelineVariable $PSItem
Если вы читали статью про функции Powershell, то знаете, что существуют параметры принимающие значения из конвейера и которые этого делать не могут. Что бы увидеть какие параметры принимают данные из конвейера используйте Get-Help:
Get-Help Get-Service -Parameter *
При создании командлетов указывается какие параметры будут принимать значения из конвейера, какие только по имени (свойству) и каким достаточно только значения. Если нам нужно подставить наши значения в определенный параметр в конвейере используется специальная переменная "$PSItem" или "$_" . Они так же используются в выражениях (ScriptBlock). Например выполнив следующую команду мы получим ошибку:
$h = [pscustomobject]@{Service='WinRM'}
$h | Get-Service
- Get-Service : Не удается найти службу с именем службы.
- Get-Service : Cannot find any service with service name.
Этот тип массивов относится к именованным, а это значит что-либо должно совпадать имя/свойство (оно Service вместо Name) либо мы должны передаваться только значение. Первый вариант это корректно переименовать хэш-таблицу:
$h = [pscustomobject]@{Name='WinRM'}
$h | Get-Service
Пример выше сработает если только именованный массив относится к PSCustomObject. Если бы это был hashtable, то была бы ошибка.
Второй вариант это напрямую передавать значение:
$h = [pscustomobject]@{Service='WinRM'}
$h.Service | Get-Service
Чаще и удобнее всего значения подставлять так:
$h = [pscustomobject]@{Service='WinRM'}
$h | Get-Service -Name {$PSItem.Service}
Еще один пример использования $PSItem и $_ в выражениях (ScriptBlock). Вам наверняка часто требуется изменить вывод команд. На примере ниже я получаю сервис, который запущен и содержит в имени "WinR":
Get-Service | where-object -FilterScript {($PSItem.Status -eq 'Running') -and ($PSItem.Name) -like '*WinR*'}
Where-Object - самый частый пример использования таких переменных, так как по умолчанию он не позволяет использовать несколько условий.
Команды
Для создания, удаления и изменения есть 5 команд, которые можно увидеть так:
Get-Command -Noun Variable
Получение списка с Get-Variable
Переменные бывают приватными и публичными. По умолчанию всегда создаются публичные. Для получения списка таких переменных и значений используйте этот командлет:
Get-Variable
Есть несколько ключей, которые мы можем использовать для фильтрации:
- Include - включает поиск по точному соответствию или маске. Например "*PS*1" может соответствовать упоминанию "AoPSlessoon1";
- Exlude - исключает упоминания по тому же принципу, что и Include;
- Name - ищет по имени. Работает так же как Include;
- ValueOnly - показывает только значения;
- Scope - тип переменной. Мы можем получить только глобальные "Global", локальные "Local" и Script.
Так мы получим переменную, которая хранит логику поведения при некритичных ошибках:
Get-Variable -Name *error* -Include *ac* -Exclude *123*
Если вы попробуете выполнить следующую команду, то увидите одну не критическую ошибку и один процесс:
Get-Process -Name "1234567","winlogon"
Учитывая предпочтения мы можем изменить переменную ErrorActionPreference так, что бы ошибка не отображалась либо что бы командлет останавливался:
$ErrorActionPreference = SilentlyContinue
При такой установке, при работе в текущей консоли, ошибки не будут отображаться. Если вы перезагрузите компьютер или закроете консоль - переменная примет значение по умолчанию. Не все системные переменные можно так изменить.
Создание с New-Variable
Я почти никогда не пользовался возможностью создания переменной через команду, но это тоже возможно. На примере ниже мы создали две одинаковых переменных:
$day1 = "Sunday"
$day1
New-Variable -Name day2 -Value "Sunday"
$day2
Мы не можем создать переменную используя пробел обычным способом:
$day 1 = "Sunday"
Такое написание приведет к ошибке "Непредвиденная лексема", но с командлетом такое пройдет:
New-Variable -Name 'day 1' -Value 'Sunday'
${day 1}
Конечно так лучше не делать и обходить такие ситуации нижним подчеркиванием.
Если вы попробуете объявить переменные с одним именем следующим способом, то получите ошибку:
$ar = 1
New-Variable -Name ar -Value 2
- New-Variable : Переменная с именем "ar" уже существует.
- New-Variable : A variable with name 'ar' already exists.
Для того что бы перезаписать значения используя командлет нужно дополнительно указать ключ -Force:
$ar = 1
New-Variable -Name ar -Value 2 -Force
$ar
Работа командлета с ключом Force будет эквивалентна этим действиям:
$ar = 1
$ar = 2
$ar
Так же можно добавить описание переменной используя параметр Description, но увидеть это описание можно только через Get-Variable.
Приватные и публичные
Мы можем создать приватные и публичные переменные используя параметр Visibility с ключами:
- Public
- Private
По умолчанию у нас всегда создаются публичные переменные (все примеры выше). Я никогда не использую приватные переменные, так как не вижу в этом смысла. Как я прочитал их используют разработчики модулей для того, что бы их нельзя было изменить. Если создать такую переменную в консоли и попытаться вызвать ее можно получить ошибку:
New-Variable -Name te -Value 3 -Visibility Private
$te
- Не удается получить доступ к переменной "'$te", так как она является частной.
- Cannot access the variable '$te' because it is a private variable.
Приватные переменные работают в рамках сеанса, в которой они созданы. Если вы запустите эту же команду в ISE или поместите в функцию - все сработает нормально:
function Test-Funct($a){
New-Variable -Name private_var -Value 1 -Visibility Private
$private_var += $a
return $private_var
}
Test-Funct 1
Ошибок не будет, если такая же приватная переменная будет работать в отдельном модуле или скрипте.
Options
В отличие от параметра Visibility, который работает в рамках сеансов, Options определяет ее тип. У нас доступно 5 значений:
- None - стоит по умолчанию;
- ReadOnly - может быть удалена. Изменение возможно только с параметром Force;
- Private - переменная доступна только в текущей области (не в сеансе, как в случае с Visibility);
- AllScope - переменная будет работать в любой новой области;
- Constant - не может быть удалена или изменена.
Создав переменную для чтения мы сможем ее вызывать:
New-Variable -Name ww -Value 11 -Option ReadOnly
$ww
А попытка изменения без ключа Force приведет к ошибке:
$ww = 22
- Не удается перезаписать переменную ww, так как она является постоянной либо доступна только для чтения.
- Cannot overwrite variable ww because it is read-only or constant.
Константы работают так же, но даже с ключом Force их изменить нельзя:
New-Variable -Name ww2 -Value 11 -Option Constant
$ww2
New-Variable -Name ww2 -Value 22 -Option Constant -Force
Вы можете увидеть переменные, которые уже созданы как константы:
Get-Variable | Format-Table -Property name, options -autosize
Одна из таких переменных $PSVersionTable.
Если вы создадите обычную переменную, то она будет видна в функции:
$bf1 = 1
function Test-Funct($a){
return $bf1 + 1}
Test-Funct
Обозначив ее приватной - этого сделать не получится:
New-Variable -Name bf2 -Value 6 -Option Private
function Test-Funct($a){
return $bf2
}
Test-Funct 1
Локальные и глобальные
Если продолжить прошлый пример и попытаться создать переменную внутри функции, а затем вызвать ее снаружи - у нас это не получится сделать:
function Test-Funct($a){
$bsd = 1
}
Test-Funct 3
$bsd
Используя Scope со значением Global это можно исправить:
function Test-Funct($a){
New-Variable -Name bsd -Value 1 -Scope Global
}
Test-Funct 3
$bsd
В основном я программирую на Python, и там такой подход считается плохим. Связано это с тем, что функции не созданы для объявления переменных и если это все же происходит - скорее всего она написана не верно. Ошибка может заключаться в том, что модуль с такой переменно может быть импортирован, что приведет к ошибке.
Кроме глобальных доступны еще 3 типа областей:
- Local - значение по умолчанию;
- Script - работает в рамках скрипта или модуля;
- Private - работает в рамках одной области.
Так как по умолчанию создаются переменные Local, работающих только в своих окружениях, следующий скрипт, с одинаковой переменной, выдаст два разных значения:
$name_1 = 'Variable name 1'
function Test-Funct($a){
$name_1 = 'Variable name 2'
return $name_1
}
Test-Funct
$name_1
При этом локальные переменные доступны внутри функций:
$name_1 = 'Variable name 1'
function Test-Funct($a){
return $name_1
}
Test-Funct
Такие типы можно объявлять еще так:
$Private:myVariable1 = 1
$Global:myVariable2 = 2
Изменение с Set-Variable
Изменить значение переменной можно двумя путями. Первый - способом описанным раннее:
$val1 = 1
$val1
$val1 = 2
$val1
Аналогичный способ, но с использованием команды:
$val1 = 1
$val1
Set-Variable -Name val1 -Value 2
$val1
Если переменная не была создана раннее таким образом ее можно создать:
Set-Variable -Name val2 -Value 2
$val2
Есть возможность фильтрации и массового изменения переменных с помощью параметров описанных раннее, но вряд ли это когда-то понадобится:
- Include
- Exclude
Можно изменить типы переменных. Нужно помнить, что константу изменить нельзя например.
- Option
- Force
Очистка и удаление
Есть два командлета, которые удаляют значения:
- Clear-Variable - удалит только значение, после чего переменная будет равна Null;
- Remove-Variable - удаляет значение и саму переменную.
Если попытаться выполнить следующий скрипт покажется, что эти команды одинаковые:
$a = 1
$b = 2
Clear-Variable -Name a
Remove-Variable -Name b
$a -eq $b
Дело в том, что вызов несуществующей переменной в Powershell не приводит к ошибке. Такая переменная будет равна Null. Вот и со случаем удаления, через Remove-Variable происходит так же.
Передача в качестве параметров команды
Интересной возможностью использования переменных - это передача как параметров. Рассмотрим команду получения процессов:
Get-Process -Name *a* -IncludeUserName -ErrorAction SilentlyContinue
Команды могут содержать бесконечно длинный список аргументов. Есть возможность его сократить используя hashtable. Команда ниже выполняет ту же процедуру, что и выше:
$process = @{Name='*a*'; IncludeUserName=$True; ErrorAction='SilentlyContinue'}
Get-Process @process
Как видно мы просто передали переменную с массивом обозначив ее знаком @.
Powershell Get-Member работа с методами и свойствами объекта на примерах
Методы и функции
Переменные в Powershell это такой же объект, который имеет свои методы и свойства. Посмотреть на них можно так:
$obj_int = 1
$obj_str = 'st'
$obj_int | get-member
$obj_str | get-member
Как вы можете увидеть, в зависимости от типа данных, у нас доступны разные методы. Используя строки, например, мы можем посчитать длину:
$st = '123456'
$st.Length
Можно выполнить замену значений используя метод replace:
$st.Replace(4, 'x')
Большинство методов доступны в виде команд. Пример действия которого аналогичны предыдущему:
$st -replace '1','W'
Передача на удаленный компьютер с Invoke-WebRequest
Если вы работаете с PSRemoting, который позволяют выполнять команды на удаленном компьютере, вы попробуете сделать так:
$service = 'WinRM'
Invoke-Command -ComputerName localhost -ScriptBlock {Get-Service -Name $service}
Мы либо получим неверный вывод либо ошибки:
- Не удается проверить аргумент для параметра "Name". Аргумент пустой или имеет значение NULL.
- Cannot validate argument on parameter 'Name'. The argument is null or empty.
Для передачи переменных, а так же функций, нужно добавлять $Using:
$service = 'WinRM'
Invoke-Command -ComputerName localhost -ScriptBlock {Get-Service -Name $Using:service}
Импорт из файла
Если у вас хранятся значения в файле со следующим синтаксисом, то мы можем их импортировать:
$some_val1=1
$some_val2=2
Для этого нам нужно получить содержимое файла и объявить значение заново:
$path = "C:\data.log"
Get-Content $path | Foreach-Object{
$new_var = $_.Split('=')
New-Variable -Name $new_var[0] -Value $new_var[1] -Force
}
Обращайте внимание на ваш синтаксис, так как я почти всегда использую пробелы между равно:
$some_val1 = 1
$some_val2 = 2
Для таких случаев скрипт нужно изменить, так как Split не удалит пробелы. Одно из решений добавить в Split пробелы:
$new_var = $_.Split(' = ')
...
Подписывайтесь на наш Telegram канал
Теги: #powershell