Переменные в Powershell и работа с типами данных - объявление и вывод


30 ноября 2019


Работа с переменными в Powershell объявление типов данных и вывод

Во всех языках программирования так же как и в Powershell переменные несут ключевой характер. Переменные нужны для хранения, вывода и передачи значений. Существует множество типов переменных, каждый из которых мы разберем ниже. В этой статье будут разобраны примеры по выводу, созданию, удалению переменных и многие другие действия.

 

Создание

Переменная - это ссылка на значение, которое хранится в памяти. Каждая переменная в Powershell начинается со знака доллара "$" и может хранить числа, буквы и нижние подчеркивания. Присваивание значений осуществляется через знак равно "=". На примере ниже мы создали переменную variable со значением 8:

$variables = 8

Что бы вывести переменную в консоли ее нужно повторно написать:

$variables

Объявление переменной в Powershell

Мы можем проводить арифметические операции с разными переменными:

$variable1 = 8
$variable2 = 7
$variable1 - $variable2

Операции с переменной в Powershell

Такие операции можно проводить и со строками. На примере ниже мы выполняем сложение:

$variable1 = "один два"
$variable2 = " три"
$variable1 + $variable2

Операции с переменной в Powershell

Если выполнять сложение числа и строки, то число автоматически преобразуется в строку:

$str= "один два "
$int = 3
$str + $int 

Вывод переменной Powershell 

Такое поведение одинаково не во всех языках и таких ситуаций лучше избегать.

Перезаписать значение переменной можно так:

$variables = 1
$variables = 2
$variables

Вывод значения переменной Powershell

Переменные живут в рамках текущего сеанса. Если вы работаете через консоль - после ее закрытия они удаляться, если это работающий скрипт - до закрытия программы. Исключения только с системными переменными и импортируемыми модулями. Одна из таких переменных, которая создается каждый сеанс $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()

Посмотреть тип переменной Powershell

Назначать типы можно как значению, так и самой переменной:

[int]$num = 1
$num
$num1 = [int]1
$num1

Типы переменных Powershell

Если мы попробуем преобразовать строку в число указывая ее тип, то получим ошибку так как невозможно сделать из букв числа:

[int]$num = 'Test'
$num

Ошибки с переменными в Powershell

Есть некоторые типы, которые объявлять обязательно, так как Powershell может конвертировать в другой формат. Один из таких примеров PSCustomObject. Если мы не объявим этот тип, то получим hashtable:

$data = [PSCustomObject]@{Name='Alex'}
$data.GetType()
$data1 = @{'Name'='Alex'}
$data1.GetType()

Узнать тип переменной Powershell

Про работу с такими типами вы можете почитать в предыдущих статьях, например работа с хеш таблицами в Powershell Hashtable.

Дату так же можно объявлять несколькими путями. Это можно делать через команду и объявляя тип (используя свой стандарт времени):

$date = Get-Date
$date
[datetime]$date1 = '2019-11-26'
$date

Использование переменных с датой в Powershell 

 

Кавычки и форматирование строк

Вы можете использовать одинарные и двойные кавычки. Основное различие в том, что двойные кавычки позволяют передавать переменные внутри строки:

$ch = 1
$sd = "Lesson number $ch"
$sd
$ch = 1
$sd = 'Lesson number $ch'
$sd

Передача переменной в Powershell

Конечно можно передавать данные используя параметр форматирования:

$ch = 1
$sd = 'Lesson number {0}' -f $ch
$sd

Передача переменной в Powershell

Еще один способ с методом .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

Форматирование и передача значений в Powershell

 

Системные $_ и $PSItem и конвейер

Когда мы работаем с существующими командами Powershell бывает необходимо передать определенное значение в параметр. На примере ниже у нас есть список сервисов статус которых мы хотим получить:

$massive = @('WinRM','NetLogon')
$massive | Get-Service

Передача переменной через конвейер в Powershell

На самом деле команда сама подставляет нужные переменные под нужные параметры, которые в целом выглядят так:

$massive | Get-Service -PipelineVariable $PSItem 

PSItem в Powershell

Если вы читали статью про функции Powershell, то знаете, что существуют параметры принимающие значения из конвейера и которые этого делать не могут. Что бы увидеть какие параметры принимают данные из конвейера используйте Get-Help:

Get-Help Get-Service -Parameter *

Работа с конвейером в Powershell

При создании командлетов указывается какие параметры будут принимать значения из конвейера, какие только по имени (свойству) и каким достаточно только значения. Если нам нужно подставить наши значения в определенный параметр в конвейере используется специальная переменная "$PSItem" или "$_" . Они так же используются в выражениях (ScriptBlock). Например выполнив следующую команду мы получим ошибку:

$h = [pscustomobject]@{Service='WinRM'}
$h | Get-Service

Передача именованного массива через конвейер в Powershell

  • Get-Service : Не удается найти службу с именем службы.
  • Get-Service : Cannot find any service with service name.

Этот тип массивов относится к именованным, а это значит что-либо должно совпадать имя/свойство (оно Service вместо Name) либо мы должны передаваться только значение. Первый вариант это корректно переименовать хэш-таблицу:

$h = [pscustomobject]@{Name='WinRM'}
$h | Get-Service

Передача именованного массива через конвейер в Powershell

Пример выше сработает если только именованный массив относится к PSCustomObject. Если бы это был hashtable, то была бы ошибка.

Второй вариант это напрямую передавать значение:

$h = [pscustomobject]@{Service='WinRM'}
$h.Service | Get-Service

Вызов значения переменной в Powershell

Чаще и удобнее всего значения подставлять так:

$h = [pscustomobject]@{Service='WinRM'}
$h | Get-Service -Name {$PSItem.Service}

Вызов именованного параметра с PSItem в Powershell

Еще один пример использования $PSItem и $_ в выражениях (ScriptBlock). Вам наверняка часто требуется изменить вывод команд. На примере ниже я получаю сервис, который запущен и содержит в имени "WinR":

Get-Service | where-object -FilterScript {($PSItem.Status -eq 'Running') -and ($PSItem.Name) -like '*WinR*'}

PSItem с Where-Object в Powershell

Where-Object - самый частый пример использования таких переменных, так как по умолчанию он не позволяет использовать несколько условий.

 

Команды

Для создания, удаления и изменения есть 5 команд, которые можно увидеть так:

Get-Command -Noun Variable

Команды по работе с переменными в Powershell

Получение списка с Get-Variable

Переменные бывают приватными и публичными. По умолчанию всегда создаются публичные. Для получения списка таких переменных и значений используйте этот командлет:

Get-Variable

Получение списка системных переменных в Powershell

Есть несколько ключей, которые мы можем использовать для фильтрации:

  • Include - включает поиск по точному соответствию или маске. Например "*PS*1" может соответствовать упоминанию "AoPSlessoon1";
  • Exlude - исключает упоминания по тому же принципу, что и Include;
  • Name - ищет по имени. Работает так же как Include;
  • ValueOnly - показывает только значения;
  • Scope - тип переменной. Мы можем получить только глобальные "Global", локальные "Local" и Script.

Так мы получим переменную, которая хранит логику поведения при некритичных ошибках:

Get-Variable -Name *error* -Include *ac* -Exclude *123*

Фильтрация списка переменных в Powershell

Если вы попробуете выполнить следующую команду, то увидите одну не критическую ошибку и один процесс:

Get-Process -Name "1234567","winlogon"

Изменение системных переменных в Powershell

Учитывая предпочтения мы можем изменить переменную ErrorActionPreference так, что бы ошибка не отображалась либо что бы командлет останавливался:

$ErrorActionPreference = SilentlyContinue

При такой установке, при работе в текущей консоли, ошибки не будут отображаться. Если вы перезагрузите компьютер или закроете консоль - переменная примет значение по умолчанию. Не все системные переменные можно так изменить.

Создание с New-Variable

Я почти никогда не пользовался возможностью создания переменной через команду, но это тоже возможно. На примере ниже мы создали две одинаковых переменных:

$day1 = "Sunday"
$day1
New-Variable -Name day2 -Value "Sunday"
$day2

Создание переменной с командой Powershell

Мы не можем создать переменную используя пробел обычным способом:

$day 1 = "Sunday"

Такое написание приведет к ошибке "Непредвиденная лексема", но с командлетом такое пройдет:

New-Variable -Name 'day 1' -Value 'Sunday'
${day 1}

Создание переменной с пробелом в Powershell

Конечно так лучше не делать и обходить такие ситуации нижним подчеркиванием.

Если вы попробуете объявить переменные с одним именем следующим способом, то получите ошибку:

$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

Перезапись значения переменной в Powershell

Работа командлета с ключом Force будет эквивалентна этим действиям:

$ar = 1
$ar = 2
$ar

Перезапись значения переменной в Powershell

Так же можно добавить описание переменной используя параметр 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.

Создание приватных переменных в Powershell

Приватные переменные работают в рамках сеанса, в которой они созданы. Если вы запустите эту же команду в ISE или поместите в функцию - все сработает нормально:

function Test-Funct($a){
    New-Variable -Name private_var -Value 1 -Visibility Private
    $private_var += $a
    return $private_var
}
Test-Funct 1

Создание приватных переменных в Powershell

Ошибок не будет, если такая же приватная переменная будет работать в отдельном модуле или скрипте.

Options

В отличие от параметра Visibility, который работает в рамках сеансов, Options определяет ее тип. У нас доступно 5 значений:

  • None - стоит по умолчанию;
  • ReadOnly - может быть удалена. Изменение возможно только с параметром Force;
  • Private - переменная доступна только в текущей области (не в сеансе, как в случае с Visibility);
  • AllScope - переменная будет работать в любой новой области;
  • Constant - не может быть удалена или изменена.

Создав переменную для чтения мы сможем ее вызывать:

New-Variable -Name ww -Value 11 -Option ReadOnly
$ww

А попытка изменения без ключа Force приведет к ошибке:

$ww = 22

Создание переменной только для чтения в Powershell

  • Не удается перезаписать переменную 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

Создание константы в Powershell

Вы можете увидеть переменные, которые уже созданы как константы:

Get-Variable | Format-Table -Property name, options -autosize

Одна из таких переменных $PSVersionTable.

Если вы создадите обычную переменную, то она будет видна в функции:

$bf1 = 1
function Test-Funct($a){
    return $bf1 + 1}
Test-Funct

Вызов переменной в функции Powershell

Обозначив ее приватной - этого сделать не получится:

New-Variable -Name bf2 -Value 6 -Option Private
function Test-Funct($a){
    return $bf2
}
Test-Funct 1

Создание приватной переменной в Powershell

Локальные и глобальные

Если продолжить прошлый пример и попытаться создать переменную внутри функции, а затем вызвать ее снаружи - у нас это не получится сделать:

function Test-Funct($a){
    $bsd = 1
}
Test-Funct 3
$bsd

Область видимости переменной Powershell

Используя Scope со значением Global это можно исправить:

function Test-Funct($a){
    New-Variable -Name bsd -Value 1 -Scope Global
}
Test-Funct 3
$bsd

Создание глобальной переменной в Powershell

В основном я программирую на 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

Переменные Powershell

При этом локальные переменные доступны внутри функций:

$name_1 = 'Variable name 1'
function Test-Funct($a){
    return $name_1
}
Test-Funct

Переменные Powershell

Такие типы можно объявлять еще так:

$Private:myVariable1 = 1
$Global:myVariable2 = 2

Изменение с Set-Variable

Изменить значение переменной можно двумя путями. Первый - способом описанным раннее:

$val1 = 1
$val1
$val1 = 2
$val1

Изменение переменных Powershell

Аналогичный способ, но с использованием команды:

$val1 = 1
$val1
Set-Variable -Name val1 -Value 2
$val1

Изменение переменных Powershell

Если переменная не была создана раннее таким образом ее можно создать:

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

Дело в том, что вызов несуществующей переменной в Powershell не приводит к ошибке. Такая переменная будет равна Null. Вот и со случаем удаления, через Remove-Variable происходит так же.

 

Передача в качестве параметров команды

Интересной возможностью использования переменных - это передача как параметров. Рассмотрим команду получения процессов:

Get-Process -Name *a* -IncludeUserName -ErrorAction SilentlyContinue

Команда Powershell

Команды могут содержать бесконечно длинный список аргументов. Есть возможность его сократить используя hashtable. Команда ниже выполняет ту же процедуру, что и выше:

$process = @{Name='*a*'; IncludeUserName=$True; ErrorAction='SilentlyContinue'}
Get-Process @process

Передача переменной в виде параметров Powershell

Как видно мы просто передали переменную с массивом обозначив ее знаком @.

 

Методы и функции

Переменные в Powershell это такой же объект, который имеет свои методы и свойства. Посмотреть на них можно так:

$obj_int = 1
$obj_str = 'st'
$obj_int | get-member
$obj_str | get-member

Методы по работе с переменными в Powershell

Как вы можете увидеть, в зависимости от типа данных, у нас доступны разные методы. Используя строки, например, мы можем посчитать длину:

$st = '123456'
$st.Length

Длина строки Powershell

Можно выполнить замену значений используя метод replace:

$st.Replace(4, 'x')

Замена строки Powershell

Большинство методов доступны в виде команд. Пример действия которого аналогичны предыдущему:

$st -replace '1','W'

Замена строки Powershell

 

Передача на удаленный компьютер с 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
}

Открытие файла с переменными в Powershell

Обращайте внимание на ваш синтаксис, так как я почти всегда использую пробелы между равно:

$some_val1 = 1
$some_val2 = 2

Для таких случаев скрипт нужно изменить, так как Split не удалит пробелы.  Одно из решений добавить в Split пробелы:

$new_var = $_.Split(' = ')
...

Теги: #powershell


Популярные тэги
Каналы
О блоге
Этот блог представляет собой конспекты выученного материала, преобретенного опыта и лучшие практики в системном администрировании и программировании.