Powershell - мова не чутлива до регістру. for, FOR, FoR, fOr - йому все одно.

Командна оболонка

ред.

Команди Powershell можна виконувати в своїй командній оболонці, подібній до cmd.exe, а можна в середовищі що називається Powershell ISE, і постачається разом з Powershell 2.0 та вище.

Коментарі

ред.

В PowerShell, все в рядку що знаходиться справа від символу "дієз" буде вважатись коментарем.

це код # це коментар
# цей ввесь рядок - коментар

У версіях PowerShell більших за 2.0 (а це майже всі версії) можна використовувати блоковий коментар

це код <# а це - коментар #> і знову код.
<# 
а тут взагалі
кожен рядок - коментар
#>

Командлети

ред.

Команди Powershell називаються командлетами і складаються з двох частин розділених дефісом - дієслова та об’єкта. Наприклад командлет що дозволяє отримати список всіх доступних командлетів:

Get-Command -commandType cmdlet

Можна отримати інформацію про командлет використовуючи командлет Get-Help[tn 1], після якого вказавши назву командлета. Get-Help Get-Command дозволяє отримати всі розділи довідки.

Командлети мають параметри які дозволяють їм отримати додаткову інформацію про те що і як потрібно робити. Параметри бувають порядкові, іменовані та перемикачі.

Наприклад команда Get-ChildItem[tn 2], яка перелічує дітей вузла якоїсь деревовидної структури (наприклад вміст каталогу файлової системи), має параметри:

  • -path - іменований а також порядковий (перший) параметр що вказує чиїх дітей треба перелічити (наприклад Get-ChildItem C:\.
  • -recurse - перемикач який дозволяє вказати командлету що треба також повернути дітей дітей.

Псевдоніми

ред.

Якщо ви постійно працюєте в PowerShell, то щоразу набирати Get-ChildItem буде дуже втомливо, тому для неї створені такі псевдоніми як ls та dir.

Список доступних псевдонімів можна отримати командою ls alias: (отримати вміст віртуального диска alias) або Get-Alias.

Створити новий псевдонім можна за допомогою команди Set-Alias, наприклад:

Set-Alias np notepad.exe

Видалити псевдонім np:

Remove-Item alias:\np

Змінні

ред.

Змінні, як і все інше в PowerShell нечутливі до регістру. Назва змінної може містити що завгодно, але якщо вона міститиме символи що можуть неоднозначно сприйнятись (наприклад прогалики) то її потрібно помістити в фігурні дужки. Кожна змінна позначається знаком долара ($) за яким йде її назва.

Змінні створюються при першому присвоєнні, наприклад:

$a = $b = 0 # присвоїти обом нуль
$a , $b = 2, 3 # $а буде 2, а $b - 3

Отримати список всіх наявних змінних можна за допомогою команди ls variable: (отримати вміст віртуального диска variable) або Get-Variable.

Щоб перевірити існування змінної $a можна так:

Test-Path variable:\a

Константи

ред.

Окрім звичайного присвоєння, змінну можна створити за допомогою командлета New-Variable, що дозволяє передати їй деякі додаткові параметри, наприклад вказати режим лише для читання:

New-Variable -Name pi -Value 3.1415926 -Option ReadOnly

Щоб видалити константу, доведеться передати в Remove-Item ключ -Force.

Змінні середовища

ред.

Щоб отримати доступ до змінних середовища, використовують префікс env:, наприклад $env:path.

Щоправда кожен скрипт має свою копію набору змінних середовища, тому зміна їх значень призводить лише до змін всередині скрипта. Якщо ви захочете змінити змінну середовища на довший термін для користувача чи для всієї системи - використайте фукнцію

[environment]::SetEnvironmentVariable("path", $new_value, "user")

де перший параметр - назва змінної, другий - значення, а третій - "user", якщо змінну треба встановити для користувача, і "machine" - якщо для всієї машини.

Інші цікаві змінні

ред.

Змінні виду ${C:\path\to.file} дозволяють отримати доступ до файлів.

Підстановка в рядки

ред.

Якщо рядок записується в подвійних лапках, то в нього можна підставляти вирази та змінні PowerShell.

Щоб підставити в рядок змінну $x пишуть "Значення x: $x".

Вираз можна підставити в рядок, якщо помістити його в дужки перед якими поставити знак долара. Це називають прямою змінною (direct variable), бо її значення визначається її "іменем". Наприклад:

"$(2+2)" - дасть на виході "4".

Натомість '$(2+2)' - так і буде '$(2+2)'.

Типи

ред.

Також, кожній змінній можна задати тип (хоча це не обов’язково), написавши його назву перед її оголошенням в квадратних дужках. Змінна маючи пов’язаний тип, буде намагатись привести значення які їй присвоюють до вказаного типу. Наприклад:

[bool]$x = $True
$x = 0 # x дорівнюватиме $False

Можливі також складніші приведення:

[datetime]$date = '2014.04.01 12:45"

Щоб визначити тип змінної, викличте її метод GetType:

$date.GetType().Name

Щоб визначити приналежність змінної типу, можна використати оператор -is, наприклад:

"hello" -is [string] # True

Стрічки

ред.

$env:path - це стрічка що містить шляхи до програм розділені двокрапкою. Її можна перевторити на список стрічок, викликавши метод split():

$env:path.split(';')

Інші корисні командлети

ред.
  • Start-Sleep -s $seconds -m $miliseconds - призупиняє виконання скрипта на $seconds секунд, або $miliseconds мілісекунд.[tn 3]
  • Clear-Host - очищає консоль.[tn 4]

Масиви

ред.

Масиви - це змінні типу [array]. Багато команд в PowerShell повертають в результаті своєї роботи саме масиви. Наприклад:

$a = ipconfig
$a -is [array] # True

Як і в інших мовах програмування, отримати доступ до елементу масиву можна за допомогою квадратних дужок, при цьому нумерація починається з нуля:

$a[1] # отримати ДРУГИЙ елемент масиву

$a.Count, або $a.Length - кількість елементів масиву.

Таким чином отримати останній елемент масиву можна наступним чином: $a[$a.Count - 1]. Але є й простіший спосіб: $a[-1]. Це нагадує мову Python.

Масив можна створити перелічивши елементи через кому:

# елементи не обов’язково повинні мати однакові типи
$a = 1, 2, 3, "hello", "world"

або задавши як діапазон:

$a = 1..4

Порожній, та масив з одного елемента створюються за допомогою @() та @($element) відповідно.

І хоча масиви в PowerShell, як і в інших динамічних мовах програмування гетерогенні, тобто можуть містити елементи будь-якого типу, можна задати типізований масив, який зможе містити лише елементи певного типу, наприклад:

[int[]]$a = 1, 2

Хеші

ред.

Хеші, або асоціативні масиви - це масиви, індексами яких можуть виступати не тільки числа, а довільні значення. Записуються парами ключ-значення, розліленими крапкою з комою і поміщеними фігурні дужки, перед якими стоїть "собачка":

$user = @{name="admin"; id=1;}

Отримати значення за ключем можна кількома способами:

$user['name']
$user.name

$key = 'name' # назва ключа
$user[$key]
$user.$key # те саме що $user.name, бо $key містить таке значення

$user.keys - масив всіх ключів хеша. Значення за ключами можна отримувати не по одному, а зразу цілим масивом, якщо попросити масив ключів: $user[$user.keys] - всі значення хеша.

Щоб додати новий ключ в хеш, достатньо зробити присвоєння:

$user.date = Get-Date

$user.keys містить всі ключі хеша, а всі значення можна отримати за допомогою виразу $user[$user.keys].

Таким чином ми можемо передавши масив ключів отримувати масив значень:

$NATO = @{
    "A" = "Alpha";
    "B" = "Bravo";
    # ...
    "Z" = "Zulu";
}
$NATO[([string[]]"BUNYK".ToCharArray())]
# дає: Bravo, Uniform, November, Yankee, Kilo

Як і в Python, хеші та масиви передаються за посиланням а не за значенням, тобто можлива така помилка:

$user1 = @{
    name = 'Taras'
}

$user2 = $user1
$user2.name = 'Oleg'

Write-Host $user1.name # теж змінено на Oleg

Щоб цього уникнути, використовують метод Clone():

$user1 = @{
    name = 'Taras'
}

$user2 = $user1.Clone()
$user2.name = 'Oleg'

Write-Host $user1.name # тепер 'Taras', як і було.

Труби

ред.

Powershell дозволяє передавати вихід однієї команди на вхід іншої, за допомогою оператора "|" (труба). Типові командлети що вміють обробляти послідовність:

  • Format-Table - форматує вивід як таблицю. При цьому може приймати список атрибутів для колонок таблиці, або скриптблок[tn 5]
PS C:\Users\tbunyk> ls | Select-Object -first 1 | Format-Table

    Directory: C:\Users\tbunyk

Mode                LastWriteTime     Length Name                                                                                                                                      
----                -------------     ------ ----                                                                                                                                      
d----        10.06.2013     13:17            .cache               
  • Format-List - форматує кожен об’єкт у вигляді списку пар атрибут-значення, розділених двокрапкою.[tn 6]
PS C:\Users\tbunyk> ls | Select-Object -first 1 | Format-List

    Directory: C:\Users\tbunyk

Name           : .cache
CreationTime   : 10.06.2013 11:40:39
LastWriteTime  : 10.06.2013 13:17:42
LastAccessTime : 10.06.2013 13:17:42
  • Where-Object (також має псевдонім (?) - приймає як параметр скриптблок, і повертає лише ті об’єкти для яких умова описана в тому скриптблоці повертає істину. Наприклад 1..10 | Where-Object { $_ -lt 5 } - лише числа менші 5. [tn 7]
  • ForEach-Object (також має псевдонім (%) - приймає як параметр скриптблок, і повертає результати виконання скриптблока до кожного об’єкта що передається в трубу (до якого всередині скриптблоку звертаються як $_). [tn 8]
  • Select-Object - приймає послідовність об’єктів, і повертає послідовність об’єктів, при цьому залишаючи лише ті атрибути, які були вказані в його параметрах. Також може приймати параметри -Fist $N, що вказує повертати тільки перших N об’єктів, та -Last $N, що поверне лише останніх N об’єктів.[tn 9]
  • Out-File, або > - перенаправляє вивід командлета в файл.[tn 10]
  • Out-Null - перенаправляє вивід в нікуди. Може знадобитись наприклад для того аби приховати вивід зайвих даних скриптом.[tn 11]
  • Out-Host - виводить дані в термінал. Має параметр -paging, що дозволяє посторінково виводити дані. (Як more в Unix)[tn 12]
  • Out-String - виводить дані в рядок. [tn 13]

Умовні оператори

ред.

В Powershell є два види умовних операторів: if та switch. І ще є фільтр Where-Object, але це просто командлет, а не конструкція мови.

Оператор if:

if (умова) {
    зробити щось;
} elseif (інша умова) {
    зробити щось інше;
} else {
    зробити хоч щось;
}

Оператор switch:

$value = "Close";
switch -case ($value) {
    "open" { Write-Host "Opening.." }
    "close" { Write-Host "Closing.." }
    default { Write-Host "Don't know how to $value" }
}

Завдяки ключу -case, порівняння буде чутливим до регістру, і ми отримаємо на виході:

Don't know how to Close

Якщо його забрати - порівняння буде ігнорувати регістр в рядків. Для нерядкових змінних цей ключ не має сенсу.

Ключ -wildcard дозволяє робити порівняння з маскою.

switch -wildcard ($value) {
    "*@gmail.com" { Write-Host "Gmail" }
    "*@ukr.net" { Write-Host "Freemail" }
}

Ключ -regex дозволяє порівнювати значення з регулярними виразами.

Також в -switch замість значень можна писати умови:

switch ($value) {
    { $_ -le 5 <# умова #> } { <# код #> }
}

Умова є булевим виразом, про який мова піде далі.

Булеві вирази

ред.

Cимвол = означає присвоєння, а <, > - перенаправлення потоків, тому для порівняння значень використовують наступні оператори:

  • -eq (equal) - дорівнює
  • -ne (not equal) - не дорівнює
  • -gt (greater then) - більше
  • -lt (less then) - менше
  • -ge (greater equals) - більше або дорівнює
  • -le (less equals) - менше або дорівнює

Наприклад:

2 + 2 -eq 4 # True

Порівняння рядків ігнорують регістр:

"Alpha" -eq "alpha" # True

Коли потрібно явно задати чутливість або не чутливість до регістру, в порівняннях використовують префікси "c" (case-sensitive, чутливий до регістру) та "i" (insensitive, ignore - нечутливий, ігнорувати регістр).

"Alpha" -ceq "alpha" # False

Крім того є оператори які показують чи можна знайти елемент в масиві:

1, 2, 4 -contains 1 # True
1, 2, 4 -notcontains 3 # True

А також, звісно логічні оператори:

"2b" -or -not "2b" # True

not або ! - логічне ні, -and - і, -or - або, -xor - виключне або.

Цикли

ред.

Циклів в PowerShell багато видів:

  • while
  • do while
  • for
  • ForEach
  • І вже раніше згаданий ForEach-Object теж можна і варто використовувати як цикл.
  • І навіть switch може працювати як цикл!

Цикл while:

while (умова) {
    // робити дію
}

Цикл do while дуже корисний тим, що перевіряє умову після виконання тіла циклу, і якщо вона істинна - продовжує:

do {
    $input = Read-Host "Email: "
} while (! ($input -like "*@*.*"));

Цикл for відомий всім з таких мов як C, чи JavaScript:

for($i = 0; $i -lt 10; $i++) {
    Write-Host $i
}

Але не обов’язково використовувати for лише для цілих лічильників циклу. Можна використати його як звичайний цикл з передумовою, наприклад щоб пробігтись по всіх рядках файлу:

for(
    $file=[system.io.file]::OpenText('C:\shell_snippets.ps1');
    !($file.EndOfStream);
    $line=$file.ReadLine()
) {
    write-Host $line
}

foreach дозволяє проітерувати по кожному елементі послідовності:

foreach($i in 1..10) {
    Write-Host $i
}

ForEach-Object працює аналогічно, тільки отримує послідовність крізь трубу:

1..10 | ForEach-Object {
    Write-Host $_
}

Оператор switch, коли йому передати масив, виконує свої блоки для кожного з елементів масиву:

switch (1..10) {
    1 { Write-Host "Перше число!"} 
    {$_  % 2 -eq 0 } { Write-Host "$_ - парне" }
    {$_  % 3 -eq 0 } { Write-Host "$_ - ділиться на три" }
    default { Write-Host "$_ - нічого особливого" }
}

Варто зауважити що для 6 виконаються обидва блоки. Щоб цього уникнути в звичайному switch можна закінчувати блок командою break, але в циклі таке призведе до закінчення циклу:

{$_  % 2 -eq 0 } { Write-Host "$_ - парне"; break }

break[tn 14] (вийти з циклу) та continue[tn 15] (перервати виконання поточної ітерації і почати від початку наступної) можна використовувати у всіх циклах окрім командлета ForEach-Object.

Наприклад щоб вивести всі числа крім 5:

foreach($i in 1..10) {
    if($i -eq 5) { continue };
    Write-Host $i
}

break та continue можна вказувати з яким циклом вони працюють. Наприклад якщо маємо два вкладені цикли, ми можемо позначити їх мітками:

:outer_cycle foreach(...) {
    :inner_cycle foreach(...) {
        if (...) { break outer_cycle }
    }
}

Функції

ред.

Функції, як і командлети викликаються без дужок:

function hello {
    Write-Host "Привіт, світе!"
}
hello
# Привіт, світе!

Навіть якщо є аргументи:

function hello2($username) {
    Write-Host "Привіт, $username!"
}

hello2 Тарас
# Привіт, Тарас!

$args містить масив аргументів, або $null

return писати не обов’язково, функція сама поверне як результат значення останнього виразу:

function subtract($a, $b) {
    $a - $b 
}

Аргументи можна передати іменовано:

subtract -a 3 -b 2

або порядково:

subtract 3 2

Якщо передати більше аргументів ніж вказано при описі - вони попадуть в $args.

Параметрам можна задавати тип:

function add([int]$value ...

Одним з типів є аргумент - перемикач:

function do_something([switch]$debug) {
...

do_something -debug

Ще одним цікавим типом є посилання [ref]. За допомогою передачі за посиланням можна зробити функцію яка змінюватиме значення своїх аргументів:

function inc([ref]$x) {
    $x.Value += 1
}

$a = 1
inc ([ref]$a) 
Write-Host $a
# 2

Можна також передати для аргумента значення за замовчуванням:

function inc([ref]$x, $d=1) {
    $x.Value += $d
}

Щоб зробити аргумент функції обов’язковим, можна задати йому значенням за замовчуванням вираз, який генеруватиме виняток:

function save($fn=$(throw "Filename not passed";) {
...

Записати код функції в файл:

$function:hello > myscript.ps1

Фільтри

ред.

Фільтр - це функція що є аналогом програми фільтра в UNIX. Фільтри в PowerShell бувають двох видів:

  • повільні послідовнісні - такі що потребують читання всіх даних в пам’ять, перед видачею результату (наприклад сортування)
  • швидкі потокові (аналог генераторів в Python) - такі які одразу опрацьовують кожен елемент вхідної послідовності.

Повільні фільтри - це звичайні функції, дані яким приходять через трубу. Всередині функції ці дані містяться в масиві $input:

function squares {
    foreach($elem in $input) {
        $elem * $elem;
    }
}

1..10 | squares

Швидкі фільтри можна описати за допомогою ключового слова filter:

filter squares {
    $_ * $_
}
1..10 | squares

Або блоку process всередині функції:

function squares {
    process {
        $_ * $_
    }
}

Блок process використовується разом з блоками begin, end для ініціалізації та завершення роботи фільтра:

function accumulate {
    begin {
        $sum = 0;
    }
    process {
        $sum += $_;
        $sum;
    }
    end {
        Write-Host "Total sum: $sum"
    }
}

1..10 | accumulate

Файли

ред.

Як ми вже казали, Out-File, або > - перенаправляє вивід з труби в файл.[tn 10] Йому, якщо не використовувати форму >, можна передавати параметри, наприклад -Append - дописувати дані в файл, замість того щоб переписувати його за замовчуванням

Add-Content -Path $file -Value $value - дописує до файла певне значення, наприклад рядок.[tn 16]

Виконання скрипта

ред.

Якщо при виконанні скрипта ви отримуєте наступну помилку:

PS C:\Documents and Settings\adminaccount> . .\script.ps1
File C:\Documents and Settings\adminaccount\lpu.ps1 cannot be loaded because th
e execution of scripts is disabled on this system. Please see "get-help about_s
igning" for more details.
At line:1 char:2
+ . <<<<  .\lpu.ps1
    + CategoryInfo          : NotSpecified: (:) [], PSSecurityException
    + FullyQualifiedErrorId : RuntimeException

То це означає що поточна політика виконання не дозволяє скриптам виконуватись[tn 17]. Щоб отримати політику виконання яка зараз встановлена на вашому комп’ютері, наберіть Get-ExecutionPolicy.

Set-ExecutionPolicy remotesigned

Віддалені сесії

ред.

Дозволити віддалені сесії на сервері:[tn 18]

Enable-PSRemoting -SkipNetworkProfileCheck

-SkipNetworkProfileCheck потрібно лише якщо у вас Network location - Public. Для Home чи Work - не потрібно.

Почати віддалену сесію:[tn 19]

$cred = Get-Credential -Credential $username
Enter-PSSession -ComputerName $hostname -Credential $cred

Exit-PSSession - вийти з віддаленої сесії.[tn 20]

Виконати шматок коду на віддаленому сервері: [tn 21]

Invoke-Command -ComputerName $hostname -Credential $cred -ScriptBlock { commands }

Спосіб задавати логін та пароль неінтерактивно:

function credential([String]$user, [String]$password) {
	if($password -eq $null) {
		$pass = Read-Host "Password for $($user): " -AsSecureString
	} else {
		$pass = ConvertTo-SecureString String $password AsPlainText -Force
	}

	New-Object TypeName System.Management.Automation.PSCredential ArgumentList $user, $pass
}

Enter-PSSession -ComputerName 192.168.173.206 -Credential $(credential 'Administrator' 'password')

Запрошення до вводу

ред.

Вигляд запрошення до вводу в інтерактивній сесії можна змінити, описавши функцію prompt. Наприклад:

function Prompt 
{
	$date = Get-Date
	$location = Get-Location
	"$($date.Hour):$($date.Minute) $location>"
}

Провайдери Powershell

ред.

Get-PSProvider - отримати список провайдерів.


Реєстр

ред.

Реєстр складається з ключів та значень. Ключі подібні на директорії і містять інші ключі або значення. Значення – подібні на файли і мають ім’я, тип та власне саме значення. Ключ може містити багато значень. Колись у 16-ти розрядному реєстрі Windows 3 ключі могли містити лише одне значення стрічкового типу, власне тому називались ключами, а реєстр був більш схожий на асоціативний масив. Тепер самі “ключі” стали асоціативними масивами що містять імена значень, але назва залишилась. Також, для зворотньої сумісності кожен ключ містить значення за замовчуванням, ім’ям якого є порожній рядок.

Всі дані реєстру містяться в кількох кореневих ключах, найважливішими з яких є HKEY_CURRENT_USER, який містить налаштування для залогіненого на даний момент користувача та HKEY_LOCAL_MACHINE, який містить налаштування загалом для машини.

Доступні кореневі ключі реєстру можна отримати за допомогою команди Get-PSDrive, яка перелічує диски доступні для PowerShell. І це не тільки диски файлової системи, а й диски що містять всі змінні, середовище, а також ключі реєстру. PowerShell надає до реєстру такий самий інтерфейс як до файлової системи. Щоб переглянути ключі в налаштуваннях локальної машини досить написати:

Set-Location HKLM:\ # або cd
Get-ChildItem # або ls

Давайте збережемо якісь дані в HKLM:\Software\Test. Перевіримо що такий ключ існує:

cd HKLM:\SOFTWARE
Test-Path Test # Якщо не існує – дасть False


   if (!(Test-Path HKLM:\SOFTWARE\zenoss\logs)) {
       if (!(Test-Path HKLM:\SOFTWARE\zenoss)) {
           New-Item HKLM:\SOFTWARE\zenoss
       }
       New-Item HKLM:\SOFTWARE\zenoss\logs
   }

Event Log

ред.

Get-EventLog -list - отримати список логів.[tn 22]

Get-EventLog $name -newest 3 - отримати три найновіші записи з логу $name.

New-EventLog -LogName $name -Source manual - створити лог з джерелом manual. [tn 23]

Remove-EventLog $name - видалити лог $name. [tn 24]

Записати повідомлення в лог:

Write-EventLog -LogName $name -Source manual -Message "Hello world!" -EventId 0 -EntryType information

EntryType може бути Error, Warning, Information, SuccessAudit, або FailureAudit. [tn 25]