PowerShell

Личный сайт Go-разработчика из Казани

PowerShell is the Windows scripting language and configuration management framework from Microsoft built on the .NET Framework. Windows 7 and up ship with PowerShell.
Nearly all examples below can be a part of a shell script or executed directly in the shell.

A key difference with Bash is that it is mostly objects that you manipulate rather than plain text. After years of evolving, it resembles Python a bit.

Read more here.

Powershell as a Language:

1# Single line comments start with a number symbol. 2 3<# 4 Multi-line comments 5 like so 6#> 7 8 9#################################################### 10## 1. Primitive Datatypes and Operators 11#################################################### 12 13# Numbers 143 # => 3 15 16# Math 171 + 1 # => 2 188 - 1 # => 7 1910 * 2 # => 20 2035 / 5 # => 7.0 21 22# Powershell uses banker's rounding, 23# meaning [int]1.5 would round to 2 but so would [int]2.5 24# Division always returns a float. 25# You must cast result to [int] to round. 26[int]5 / [int]3 # => 1.66666666666667 27[int]-5 / [int]3 # => -1.66666666666667 285.0 / 3.0 # => 1.66666666666667 29-5.0 / 3.0 # => -1.66666666666667 30[int]$result = 5 / 3 31$result # => 2 32 33# Modulo operation 347 % 3 # => 1 35 36# Exponentiation requires longform or the built-in [Math] class. 37[Math]::Pow(2,3) # => 8 38 39# Enforce order of operations with parentheses. 401 + 3 * 2 # => 7 41(1 + 3) * 2 # => 8 42 43# Boolean values are primitives (Note: the $) 44$True # => True 45$False # => False 46 47# negate with ! 48!$True # => False 49!$False # => True 50 51# Boolean Operators 52# Note "-and" and "-or" usage 53$True -and $False # => False 54$False -or $True # => True 55 56# True and False are actually 1 and 0 but only support limited arithmetic. 57# However, casting the bool to int resolves this. 58$True + $True # => 2 59$True * 8 # => '[System.Boolean] * [System.Int32]' is undefined 60[int]$True * 8 # => 8 61$False - 5 # => -5 62 63# Comparison operators look at the numerical value of True and False. 640 -eq $False # => True 651 -eq $True # => True 662 -eq $True # => False 67-5 -ne $False # => True 68 69# Using boolean logical operators on ints casts to booleans for evaluation. 70# but their non-cast value is returned 71# Don't mix up with bool(ints) and bitwise -band/-bor 72[bool](0) # => False 73[bool](4) # => True 74[bool](-6) # => True 750 -band 2 # => 0 76-5 -bor 0 # => -5 77 78# Equality is -eq (equals) 791 -eq 1 # => True 802 -eq 1 # => False 81 82# Inequality is -ne (notequals) 831 -ne 1 # => False 842 -ne 1 # => True 85 86# More comparisons 871 -lt 10 # => True 881 -gt 10 # => False 892 -le 2 # => True 902 -ge 2 # => True 91 92# Seeing whether a value is in a range 931 -lt 2 -and 2 -lt 3 # => True 942 -lt 3 -and 3 -lt 2 # => False 95 96# (-is vs. -eq) -is checks if two objects are the same type. 97# -eq checks if the objects have the same values, but sometimes doesn't work 98# as expected. 99# Note: we called '[Math]' from .NET previously without the preceeding 100# namespaces. We can do the same with [Collections.ArrayList] if preferred. 101[System.Collections.ArrayList]$a = @() # Point a at a new list 102$a = (1,2,3,4) 103$b = $a # => Point b at what a is pointing to 104$b -is $a.GetType() # => True, a and b equal same type 105$b -eq $a # => None! See below 106[System.Collections.Hashtable]$b = @{} # => Point b at a new hash table 107$b = @{'one' = 1 108 'two' = 2} 109$b -is $a.GetType() # => False, a and b types not equal 110 111# Strings are created with " or ' but " is required for string interpolation 112"This is a string." 113'This is also a string.' 114 115# Strings can be added too! But try not to do this. 116"Hello " + "world!" # => "Hello world!" 117 118# A string can be treated like a list of characters 119"Hello world!"[0] # => 'H' 120 121# You can find the length of a string 122("This is a string").Length # => 16 123 124# You can also format using f-strings or formatted string literals. 125$name = "Steve" 126$age = 22 127"He said his name is $name." 128# => "He said his name is Steve" 129"{0} said he is {1} years old." -f $name, $age 130# => "Steve said he is 22 years old" 131"$name's name is $($name.Length) characters long." 132# => "Steve's name is 5 characters long." 133 134# Strings can be compared with -eq, but are case insensitive. We can 135# force with -ceq or -ieq. 136"ab" -eq "ab" # => True 137"ab" -eq "AB" # => True! 138"ab" -ceq "AB" # => False 139"ab" -ieq "AB" # => True 140 141# Escape Characters in Powershell 142# Many languages use the '\', but Windows uses this character for 143# file paths. Powershell thus uses '`' to escape characters 144# Take caution when working with files, as '`' is a 145# valid character in NTFS filenames. 146"Showing`nEscape Chars" # => new line between Showing and Escape 147"Making`tTables`tWith`tTabs" # => Format things with tabs 148 149# Negate pound sign to prevent comment 150# Note that the function of '#' is removed, but '#' is still present 151`#Get-Process # => Fail: not a recognized cmdlet 152 153# $null is not an object 154$null # => None 155 156# $null, 0, and empty strings and arrays all evaluate to False. 157# All other values are True 158function Test-Value ($value) { 159 if ($value) { 160 Write-Output 'True' 161 } 162 else { 163 Write-Output 'False' 164 } 165} 166 167Test-Value ($null) # => False 168Test-Value (0) # => False 169Test-Value ("") # => False 170Test-Value [] # => True 171# *[] calls .NET class; creates '[]' string when passed to function 172Test-Value ({}) # => True 173Test-Value @() # => False 174 175 176#################################################### 177## 2. Variables and Collections 178#################################################### 179 180# Powershell uses the "Write-Output" function to print 181Write-Output "I'm Posh. Nice to meet you!" # => I'm Posh. Nice to meet you! 182 183# Simple way to get input data from console 184$userInput = Read-Host "Enter some data: " # Returns the data as a string 185 186# There are no declarations, only assignments. 187# Convention is to use camelCase or PascalCase, whatever your team uses. 188$someVariable = 5 189$someVariable # => 5 190 191# Accessing a previously unassigned variable does not throw exception. 192# The value is $null by default 193 194# Ternary Operators exist in Powershell 7 and up 1950 ? 'yes' : 'no' # => no 196 197 198# The default array object in Powershell is an fixed length array. 199$defaultArray = "thing","thing2","thing3" 200# you can add objects with '+=', but cannot remove objects. 201$defaultArray.Add("thing4") # => Exception "Collection was of a fixed size." 202# To have a more workable array, you'll want the .NET [ArrayList] class 203# It is also worth noting that ArrayLists are significantly faster 204 205# ArrayLists store sequences 206[System.Collections.ArrayList]$array = @() 207# You can start with a prefilled ArrayList 208[System.Collections.ArrayList]$otherArray = @(5, 6, 7, 8) 209 210# Add to the end of a list with 'Add' (Note: produces output, append to $null) 211$array.Add(1) > $null # $array is now [1] 212$array.Add(2) > $null # $array is now [1, 2] 213$array.Add(4) > $null # $array is now [1, 2, 4] 214$array.Add(3) > $null # $array is now [1, 2, 4, 3] 215# Remove from end with index of count of objects-1; array index starts at 0 216$array.RemoveAt($array.Count-1) # => 3 and array is now [1, 2, 4] 217# Let's put it back 218$array.Add(3) > $null # array is now [1, 2, 4, 3] again. 219 220# Access a list like you would any array 221$array[0] # => 1 222# Look at the last element 223$array[-1] # => 3 224# Looking out of bounds returns nothing 225$array[4] # blank line returned 226 227# Remove elements from a array 228$array.Remove($array[3]) # $array is now [1, 2, 4] 229 230# Insert at index an element 231$array.Insert(2, 3) # $array is now [1, 2, 3, 4] 232 233# Get the index of the first item found matching the argument 234$array.IndexOf(2) # => 1 235$array.IndexOf(6) # Returns -1 as "outside array" 236 237# You can add arrays 238# Note: values for $array and for $otherArray are not modified. 239$array + $otherArray # => [1, 2, 3, 4, 5, 6, 7, 8] 240 241# Concatenate arrays with "AddRange()" 242$array.AddRange($otherArray) # Now $array is [1, 2, 3, 4, 5, 6, 7, 8] 243 244# Check for existence in a array with "in" 2451 -in $array # => True 246 247# Examine length with "Count" (Note: "Length" on arrayList = each items length) 248$array.Count # => 8 249 250# You can look at ranges with slice syntax. 251$array[1,3,5] # Return selected index => [2, 4, 6] 252$array[1..3] # Return from index 1 to 3 => [2, 3, 4] 253$array[-3..-1] # Return from last 3 to last 1 => [6, 7, 8] 254$array[-1..-3] # Return from last 1 to last 3 => [8, 7, 6] 255$array[2..-1] # Return from index 2 to last (NOT as most expect) => [3, 2, 1, 8] 256$array[0,2+4..6] # Return multiple ranges with the + => [1, 3, 5, 6, 7] 257 258# -eq doesn't compare array but extract the matching elements 259$array = 1,2,3,1,1 260$array -eq 1 # => 1,1,1 261($array -eq 1).Count # => 3 262 263# Tuples are like arrays but are immutable. 264# To use Tuples in powershell, you must use the .NET tuple class. 265$tuple = [System.Tuple]::Create(1, 2, 3) 266$tuple.Item(0) # => 1 267$tuple.Item(0) = 3 # Raises a TypeError 268 269# You can do some of the array methods on tuples, but they are limited. 270$tuple.Length # => 3 271$tuple + (4, 5, 6) # => Exception 272$tuple[0..2] # => $null (in powershell 5) => [1, 2, 3] (in powershell 7) 2732 -in $tuple # => False 274 275 276# Hashtables store mappings from keys to values, similar to (but distinct from) Dictionaries. 277# Hashtables do not hold entry order as arrays do. 278$emptyHash = @{} 279# Here is a prefilled hashtable 280$filledHash = @{"one"= 1 281 "two"= 2 282 "three"= 3} 283 284# Look up values with [] 285$filledHash["one"] # => 1 286 287# Get all keys as an iterable with ".Keys". 288$filledHash.Keys # => ["one", "two", "three"] 289 290# Get all values as an iterable with ".Values". 291$filledHash.Values # => [1, 2, 3] 292 293# Check for existence of keys or values in a hash with "-in" 294"one" -in $filledHash.Keys # => True 2951 -in $filledHash.Values # => False (in powershell 5) => True (in powershell 7) 296 297# Looking up a non-existing key returns $null 298$filledHash["four"] # $null 299 300# Adding to a hashtable 301$filledHash.Add("five",5) # $filledHash["five"] is set to 5 302$filledHash.Add("five",6) # exception "Item with key "five" has already been added" 303$filledHash["four"] = 4 # $filledHash["four"] is set to 4, running again does nothing 304 305# Remove keys from a hashtable 306$filledHash.Remove("one") # Removes the key "one" from filled hashtable 307 308 309#################################################### 310## 3. Control Flow and Iterables 311#################################################### 312 313# Let's just make a variable 314$someVar = 5 315 316# Here is an if statement. 317# This prints "$someVar is smaller than 10" 318if ($someVar -gt 10) { 319 Write-Output "$someVar is bigger than 10." 320} 321elseif ($someVar -lt 10) { # This elseif clause is optional. 322 Write-Output "$someVar is smaller than 10." 323} 324else { # This is optional too. 325 Write-Output "$someVar is indeed 10." 326} 327 328 329<# 330Foreach loops iterate over arrays 331prints: 332 dog is a mammal 333 cat is a mammal 334 mouse is a mammal 335#> 336foreach ($animal in ("dog", "cat", "mouse")) { 337 # You can use -f to interpolate formatted strings 338 "{0} is a mammal" -f $animal 339} 340 341<# 342For loops iterate over arrays and you can specify indices 343prints: 344 0 a 345 1 b 346 2 c 347 3 d 348 4 e 349 5 f 350 6 g 351 7 h 352#> 353$letters = ('a','b','c','d','e','f','g','h') 354for($i=0; $i -le $letters.Count-1; $i++){ 355 Write-Host $i, $letters[$i] 356} 357 358<# 359While loops go until a condition is no longer met. 360prints: 361 0 362 1 363 2 364 3 365#> 366$x = 0 367while ($x -lt 4) { 368 Write-Output $x 369 $x += 1 # Shorthand for x = x + 1 370} 371 372# Switch statements are more powerful compared to most languages 373$val = "20" 374switch($val) { 375 { $_ -eq 42 } { "The answer equals 42"; break } 376 '20' { "Exactly 20"; break } 377 { $_ -like 's*' } { "Case insensitive"; break } 378 { $_ -clike 's*'} { "clike, ceq, cne for case sensitive"; break } 379 { $_ -notmatch '^.*$'} { "Regex matching. cnotmatch, cnotlike, ..."; break } 380 default { "Others" } 381} 382 383# Handle exceptions with a try/catch block 384try { 385 # Use "throw" to raise an error 386 throw "This is an error" 387} 388catch { 389 Write-Output $Error.ExceptionMessage 390} 391finally { 392 Write-Output "We can clean up resources here" 393} 394 395 396# Writing to a file 397$contents = @{"aa"= 12 398 "bb"= 21} 399$contents | Export-CSV "$env:HOMEDRIVE\file.csv" # writes to a file 400 401$contents = "test string here" 402$contents | Out-File "$env:HOMEDRIVE\file.txt" # writes to another file 403 404# Read file contents and convert to json 405Get-Content "$env:HOMEDRIVE\file.csv" | ConvertTo-Json 406 407 408#################################################### 409## 4. Functions 410#################################################### 411 412# Use "function" to create new functions 413# Keep the Verb-Noun naming convention for functions 414function Add-Numbers { 415 $args[0] + $args[1] 416} 417 418Add-Numbers 1 2 # => 3 419 420# Calling functions with parameters 421function Add-ParamNumbers { 422 param( [int]$firstNumber, [int]$secondNumber ) 423 $firstNumber + $secondNumber 424} 425 426Add-ParamNumbers -FirstNumber 1 -SecondNumber 2 # => 3 427 428# Functions with named parameters, parameter attributes, parsable documentation 429<# 430.SYNOPSIS 431Setup a new website 432.DESCRIPTION 433Creates everything your new website needs for much win 434.PARAMETER siteName 435The name for the new website 436.EXAMPLE 437New-Website -Name FancySite -Po 5000 438New-Website SiteWithDefaultPort 439New-Website siteName 2000 # ERROR! Port argument could not be validated 440('name1','name2') | New-Website -Verbose 441#> 442function New-Website() { 443 [CmdletBinding()] 444 param ( 445 [Parameter(ValueFromPipeline=$true, Mandatory=$true)] 446 [Alias('name')] 447 [string]$siteName, 448 [ValidateSet(3000,5000,8000)] 449 [int]$port = 3000 450 ) 451 BEGIN { Write-Output 'Creating new website(s)' } 452 PROCESS { Write-Output "name: $siteName, port: $port" } 453 END { Write-Output 'Website(s) created' } 454} 455 456 457#################################################### 458## 5. Modules 459#################################################### 460 461# You can import modules and install modules 462# The Install-Module is similar to pip or npm, pulls from Powershell Gallery 463Install-Module dbaTools 464Import-Module dbaTools 465 466$query = "SELECT * FROM dbo.sometable" 467$queryParams = @{ 468 SqlInstance = 'testInstance' 469 Database = 'testDatabase' 470 Query = $query 471} 472Invoke-DbaQuery @queryParams 473 474# You can get specific functions from a module 475Import-Module -Function Invoke-DbaQuery 476 477 478# Powershell modules are just ordinary Posh files. You 479# can write your own, and import them. The name of the 480# module is the same as the name of the file. 481 482# You can find out which functions and attributes 483# are defined in a module. 484Get-Command -module dbaTools 485Get-Help dbaTools -Full 486 487 488#################################################### 489## 6. Classes 490#################################################### 491 492# We use the "class" statement to create a class 493class Instrument { 494 [string]$Type 495 [string]$Family 496} 497 498$instrument = [Instrument]::new() 499$instrument.Type = "String Instrument" 500$instrument.Family = "Plucked String" 501 502$instrument 503 504<# Output: 505Type Family 506---- ------ 507String Instrument Plucked String 508#> 509 510 511#################################################### 512## 6.1 Inheritance 513#################################################### 514 515# Inheritance allows new child classes to be defined that inherit 516# methods and variables from their parent class. 517 518class Guitar : Instrument 519{ 520 [string]$Brand 521 [string]$SubType 522 [string]$ModelType 523 [string]$ModelNumber 524} 525 526$myGuitar = [Guitar]::new() 527$myGuitar.Brand = "Taylor" 528$myGuitar.SubType = "Acoustic" 529$myGuitar.ModelType = "Presentation" 530$myGuitar.ModelNumber = "PS14ce Blackwood" 531 532$myGuitar.GetType() 533 534<# 535IsPublic IsSerial Name BaseType 536-------- -------- ---- -------- 537True False Guitar Instrument 538#> 539 540 541#################################################### 542## 7. Advanced 543#################################################### 544 545# The powershell pipeline allows things like High-Order Functions. 546 547# Group-Object is a handy cmdlet that does incredible things. 548# It works much like a GROUP BY in SQL. 549 550<# 551 The following will get all the running processes, 552 group them by Name, 553 and tell us how many instances of each process we have running. 554 Tip: Chrome and svcHost are usually big numbers in this regard. 555#> 556Get-Process | Foreach-Object ProcessName | Group-Object 557 558# Useful pipeline examples are iteration and filtering. 5591..10 | ForEach-Object { "Loop number $PSITEM" } 5601..10 | Where-Object { $PSITEM -gt 5 } | ConvertTo-Json 561 562# A notable pitfall of the pipeline is its performance when 563# compared with other options. 564# Additionally, raw bytes are not passed through the pipeline, 565# so passing an image causes some issues. 566# See more on that in the link at the bottom. 567 568<# 569 Asynchronous functions exist in the form of jobs. 570 Typically a procedural language, 571 Powershell can operate non-blocking functions when invoked as Jobs. 572#> 573 574# This function is known to be non-optimized, and therefore slow. 575$installedApps = Get-CimInstance -ClassName Win32_Product 576 577# If we had a script, it would hang at this func for a period of time. 578$scriptBlock = {Get-CimInstance -ClassName Win32_Product} 579Start-Job -ScriptBlock $scriptBlock 580 581# This will start a background job that runs the command. 582# You can then obtain the status of jobs and their returned results. 583$allJobs = Get-Job 584$jobResponse = Get-Job | Receive-Job 585 586 587# Math is built in to powershell and has many functions. 588$r=2 589$pi=[math]::pi 590$r2=[math]::pow( $r, 2 ) 591$area = $pi*$r2 592$area 593 594# To see all possibilities, check the members. 595[System.Math] | Get-Member -Static -MemberType All 596 597 598<# 599 This is a silly one: 600 You may one day be asked to create a func that could take $start and $end 601 and reverse anything in an array within the given range 602 based on an arbitrary array without mutating the original array. 603 Let's see one way to do that and introduce another data structure. 604#> 605 606$targetArray = 'a','b','c','d','e','f','g','h','i','j','k','l','m' 607 608function Format-Range ($start, $end, $array) { 609 [System.Collections.ArrayList]$firstSectionArray = @() 610 [System.Collections.ArrayList]$secondSectionArray = @() 611 [System.Collections.Stack]$stack = @() 612 for ($index = 0; $index -lt $array.Count; $index++) { 613 if ($index -lt $start) { 614 $firstSectionArray.Add($array[$index]) > $null 615 } 616 elseif ($index -ge $start -and $index -le $end) { 617 $stack.Push($array[$index]) 618 } 619 else { 620 $secondSectionArray.Add($array[$index]) > $null 621 } 622 } 623 $finalArray = $firstSectionArray + $stack.ToArray() + $secondSectionArray 624 return $finalArray 625} 626 627Format-Range 2 6 $targetArray 628# => 'a','b','g','f','e','d','c','h','i','j','k','l','m' 629 630# The previous method works, but uses extra memory by allocating new arrays. 631# It's also kind of lengthy. 632# Let's see how we can do this without allocating a new array. 633# This is slightly faster as well. 634 635function Format-Range ($start, $end) { 636 while ($start -lt $end) 637 { 638 $temp = $targetArray[$start] 639 $targetArray[$start] = $targetArray[$end] 640 $targetArray[$end] = $temp 641 $start++ 642 $end-- 643 } 644 return $targetArray 645} 646 647Format-Range 2 6 # => 'a','b','g','f','e','d','c','h','i','j','k','l','m'

Powershell as a Tool:

Getting Help:

1# Find commands 2Get-Command about_* # alias: gcm 3Get-Command -Verb Add 4Get-Alias ps 5Get-Alias -Definition Get-Process 6 7Get-Help ps | less # alias: help 8ps | Get-Member # alias: gm 9 10Show-Command Get-WinEvent # Display GUI to fill in the parameters 11 12Update-Help # Run as admin

If you are uncertain about your environment:

1Get-ExecutionPolicy -List 2Set-ExecutionPolicy AllSigned 3# Execution policies include: 4# - Restricted: Scripts won't run. 5# - RemoteSigned: Downloaded scripts run only if signed by a trusted publisher. 6# - AllSigned: Scripts need to be signed by a trusted publisher. 7# - Unrestricted: Run all scripts. 8help about_Execution_Policies # for more info 9 10# Current PowerShell version: 11$PSVersionTable 1# Calling external commands, executables, 2# and functions with the call operator. 3# Exe paths with arguments passed or containing spaces can create issues. 4C:\Program Files\dotnet\dotnet.exe 5# The term 'C:\Program' is not recognized as a name of a cmdlet, 6# function, script file, or executable program. 7# Check the spelling of the name, or if a path was included, 8# verify that the path is correct and try again 9 10"C:\Program Files\dotnet\dotnet.exe" 11C:\Program Files\dotnet\dotnet.exe # returns string rather than execute 12 13&"C:\Program Files\dotnet\dotnet.exe --help" # fail 14&"C:\Program Files\dotnet\dotnet.exe" --help # success 15# Alternatively, you can use dot-sourcing here 16."C:\Program Files\dotnet\dotnet.exe" --help # success 17 18# the call operator (&) is similar to Invoke-Expression, 19# but IEX runs in current scope. 20# One usage of '&' would be to invoke a scriptblock inside of your script. 21# Notice the variables are scoped 22$i = 2 23$scriptBlock = { $i=5; Write-Output $i } 24& $scriptBlock # => 5 25$i # => 2 26 27invoke-expression ' $i=5; Write-Output $i ' # => 5 28$i # => 5 29 30# Alternatively, to preserve changes to public variables 31# you can use "Dot-Sourcing". This will run in the current scope. 32$x=1 33&{$x=2};$x # => 1 34 35.{$x=2};$x # => 2 36 37 38# Remoting into computers is easy. 39Enter-PSSession -ComputerName RemoteComputer 40 41# Once remoted in, you can run commands as if you're local. 42RemoteComputer\PS> Get-Process powershell 43 44<# 45Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName 46------- ------ ----- ----- ------ -- -- ----------- 47 1096 44 156324 179068 29.92 11772 1 powershell 48 545 25 49512 49852 25348 0 powershell 49#> 50RemoteComputer\PS> Exit-PSSession 51 52<# 53 Powershell is an incredible tool for Windows management and Automation. 54 Let's take the following scenario: 55 You have 10 servers. 56 You need to check whether a service is running on all of them. 57 You can RDP and log in, or PSSession to all of them, but why? 58 Check out the following 59#> 60 61$serverList = @( 62 'server1', 63 'server2', 64 'server3', 65 'server4', 66 'server5', 67 'server6', 68 'server7', 69 'server8', 70 'server9', 71 'server10' 72) 73 74[scriptblock]$script = { 75 Get-Service -DisplayName 'Task Scheduler' 76} 77 78foreach ($server in $serverList) { 79 $cmdSplat = @{ 80 ComputerName = $server 81 JobName = 'checkService' 82 ScriptBlock = $script 83 AsJob = $true 84 ErrorAction = 'SilentlyContinue' 85 } 86 Invoke-Command @cmdSplat | Out-Null 87} 88 89<# 90 Here we've invoked jobs across many servers. 91 We can now Receive-Job and see if they're all running. 92 Now scale this up 100x as many servers :) 93#>

Interesting Projects