diff options
Diffstat (limited to 'powershell.html.markdown')
| -rw-r--r-- | powershell.html.markdown | 959 | 
1 files changed, 718 insertions, 241 deletions
| diff --git a/powershell.html.markdown b/powershell.html.markdown index f34d5b4e..318bf043 100644 --- a/powershell.html.markdown +++ b/powershell.html.markdown @@ -3,6 +3,7 @@ category: tool  tool: powershell  contributors:      - ["Wouter Van Schandevijl", "https://github.com/laoujin"] +    - ["Andrew Ryan Davis", "https://github.com/AndrewDavis1191"]  filename: LearnPowershell.ps1  --- @@ -13,104 +14,374 @@ 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. +rather than plain text. After years of evolving, it resembles Python a bit. -[Read more here.](https://technet.microsoft.com/en-us/library/bb978526.aspx) +[Read more here.](https://docs.microsoft.com/powershell/scripting/overview) -If you are uncertain about your environment: +Powershell as a Language:  ```powershell -Get-ExecutionPolicy -List -Set-ExecutionPolicy AllSigned -# Execution policies include: -# - Restricted: Scripts won't run. -# - RemoteSigned: Downloaded scripts run only if signed by a trusted publisher.  -# - AllSigned: Scripts need to be signed by a trusted publisher. -# - Unrestricted: Run all scripts. -help about_Execution_Policies # for more info -# Current PowerShell version: -$PSVersionTable -``` +# Single line comments start with a number symbol. -Getting help: +<# +  Multi-line comments +  like so +#> -``` -# Find commands -Get-Command about_* # alias: gcm -Get-Command -Verb Add -Get-Alias ps -Get-Alias -Definition Get-Process -Get-Help ps | less # alias: help -ps | Get-Member # alias: gm +#################################################### +## 1. Primitive Datatypes and Operators +#################################################### + +# Numbers +3 # => 3 + +# Math +1 + 1   # => 2 +8 - 1   # => 7 +10 * 2  # => 20 +35 / 5  # => 7.0 + +# Powershell uses banker's rounding, +# meaning [int]1.5 would round to 2 but so would [int]2.5 +# Division always returns a float.  +# You must cast result to [int] to round. +[int]5 / [int]3       # => 1.66666666666667 +[int]-5 / [int]3      # => -1.66666666666667 +5.0 / 3.0   # => 1.66666666666667 +-5.0 / 3.0  # => -1.66666666666667 +[int]$result = 5 / 3  +$result # => 2 + +# Modulo operation +7 % 3  # => 1 + +# Exponentiation requires longform or the built-in [Math] class. +[Math]::Pow(2,3)  # => 8 + +# Enforce order of operations with parentheses. +1 + 3 * 2  # => 7 +(1 + 3) * 2  # => 8 + +# Boolean values are primitives (Note: the $) +$True  # => True +$False  # => False + +# negate with ! +!$True   # => False +!$False  # => True + +# Boolean Operators +# Note "-and" and "-or" usage +$True -and $False  # => False +$False -or $True   # => True + +# True and False are actually 1 and 0 but only support limited arithmetic. +# However, casting the bool to int resolves this. +$True + $True # => 2 +$True * 8    # => '[System.Boolean] * [System.Int32]' is undefined +[int]$True * 8 # => 8 +$False - 5   # => -5 + +# Comparison operators look at the numerical value of True and False. +0 -eq $False  # => True +1 -eq $True   # => True +2 -eq $True   # => False +-5 -ne $False # => True + +# Using boolean logical operators on ints casts to booleans for evaluation. +# but their non-cast value is returned +# Don't mix up with bool(ints) and bitwise -band/-bor +[bool](0)     # => False +[bool](4)     # => True +[bool](-6)    # => True +0 -band 2     # => 0 +-5 -bor 0     # => -5 + +# Equality is -eq (equals) +1 -eq 1  # => True +2 -eq 1  # => False + +# Inequality is -ne (notequals) +1 -ne 1  # => False +2 -ne 1  # => True + +# More comparisons +1 -lt 10  # => True +1 -gt 10  # => False +2 -le 2  # => True +2 -ge 2  # => True + +# Seeing whether a value is in a range +1 -lt 2 -and 2 -lt 3  # => True +2 -lt 3 -and 3 -lt 2  # => False + +# (-is vs. -eq) -is checks if two objects are the same type. +# -eq checks if the objects have the same values. +# Note: we called '[Math]' from .NET previously without the preceeding +# namespaces. We can do the same with [Collections.ArrayList] if preferred. +[System.Collections.ArrayList]$a = @()  # Point a at a new list +$a = (1,2,3,4) +$b = $a                                 # => Point b at what a is pointing to +$b -is $a.GetType()                     # => True, a and b equal same type +$b -eq $a                               # => True, a and b values are equal +[System.Collections.Hashtable]$b = @{}  # => Point a at a new hash table +$b = @{'one' = 1  +       'two' = 2} +$b -is $a.GetType()                     # => False, a and b types not equal + +# Strings are created with " or ' but " is required for string interpolation +"This is a string." +'This is also a string.' + +# Strings can be added too! But try not to do this. +"Hello " + "world!"  # => "Hello world!" + +# A string can be treated like a list of characters +"Hello world!"[0]  # => 'H' + +# You can find the length of a string +("This is a string").Length  # => 16 + +# You can also format using f-strings or formatted string literals. +$name = "Steve" +$age = 22 +"He said his name is $name."  +# => "He said his name is Steve" +"{0} said he is {1} years old." -f $name, $age  +# => "Steve said he is 22 years old" +"$name's name is $($name.Length) characters long."  +# => "Steve's name is 5 characters long." + +# Escape Characters in Powershell +# Many languages use the '\', but Windows uses this character for  +# file paths. Powershell thus uses '`' to escape characters +# Take caution when working with files, as '`' is a +# valid character in NTFS filenames. +"Showing`nEscape Chars" # => new line between Showing and Escape +"Making`tTables`tWith`tTabs" # => Format things with tabs + +# Negate pound sign to prevent comment +# Note that the function of '#' is removed, but '#' is still present +`#Get-Process # => Fail: not a recognized cmdlet + +# $null is not an object +$null  # => None + +# $null, 0, and empty strings and arrays all evaluate to False. +# All other values are True +function Test-Value ($value) { +  if ($value) { +    Write-Output 'True' +  } +  else { +    Write-Output 'False' +  } +} -Show-Command Get-EventLog # Display GUI to fill in the parameters +Test-Value ($null) # => False +Test-Value (0)     # => False +Test-Value ("")    # => False +Test-Value []      # => True  +# *[] calls .NET class; creates '[]' string when passed to function +Test-Value ({})    # => True +Test-Value @()     # => False -Update-Help # Run as admin -``` -The tutorial starts here: +#################################################### +## 2. Variables and Collections +#################################################### + +# Powershell uses the "Write-Output" function to print +Write-Output "I'm Posh. Nice to meet you!"  # => I'm Posh. Nice to meet you! + +# Simple way to get input data from console +$userInput = Read-Host "Enter some data: " # Returns the data as a string + +# There are no declarations, only assignments. +# Convention is to use camelCase or PascalCase, whatever your team uses. +$someVariable = 5 +$someVariable  # => 5 + +# Accessing a previously unassigned variable does not throw exception. +# The value is $null by default + +# Ternary Operators exist in Powershell 7 and up +0 ? 'yes' : 'no'  # => no -``` -# As you already figured, comments start with # - -# Simple hello world example: -echo Hello world! -# echo is an alias for Write-Output (=cmdlet) -# Most cmdlets and functions follow the Verb-Noun naming convention - -# Each command starts on a new line, or after a semicolon: -echo 'This is the first line'; echo 'This is the second line' - -# Declaring a variable looks like this: -$aString="Some string" -# Or like this: -$aNumber = 5 -as [double] -$aList = 1,2,3,4,5 -$aString = $aList -join '--' # yes, -split exists also -$aHashtable = @{name1='val1'; name2='val2'} - -# Using variables: -echo $aString -echo "Interpolation: $aString" -echo "`$aString has length of $($aString.Length)" -echo '$aString' -echo @" -This is a Here-String -$aString -"@ -# Note that ' (single quote) won't expand the variables! -# Here-Strings also work with single quote - -# Builtin variables: -# There are some useful builtin variables, like -echo "Booleans: $TRUE and $FALSE" -echo "Empty value: $NULL" -echo "Last program's return value: $?" -echo "Exit code of last run Windows-based program: $LastExitCode" -echo "The last token in the last line received by the session: $$" -echo "The first token: $^" -echo "Script's PID: $PID" -echo "Full path of current script directory: $PSScriptRoot" -echo 'Full path of current script: ' + $MyInvocation.MyCommand.Path -echo "FUll path of current directory: $Pwd" -echo "Bound arguments in a function, script or code block: $PSBoundParameters" -echo "Unbound arguments: $($Args -join ', ')." -# More builtins: `help about_Automatic_Variables` - -# Inline another file (dot operator) -. .\otherScriptName.ps1 - - -### Control Flow -# We have the usual if structure: -if ($Age -is [string]) { -	echo 'But.. $Age cannot be a string!' -} elseif ($Age -lt 12 -and $Age -gt 0) { -	echo 'Child (Less than 12. Greater than 0)' -} else { -	echo 'Adult' + +# The default array object in Powershell is an fixed length array. +$defaultArray = "thing","thing2","thing3" +# you can add objects with '+=', but cannot remove objects. +$defaultArray.Add("thing4") # => Exception "Collection was of a fixed size." +# To have a more workable array, you'll want the .NET [ArrayList] class +# It is also worth noting that ArrayLists are significantly faster + +# ArrayLists store sequences +[System.Collections.ArrayList]$array = @() +# You can start with a prefilled ArrayList +[System.Collections.ArrayList]$otherArray = @(4, 5, 6) + +# Add to the end of a list with 'Add' (Note: produces output, append to $null) +$array.Add(1) > $null    # $array is now [1] +$array.Add(2) > $null    # $array is now [1, 2] +$array.Add(4) > $null    # $array is now [1, 2, 4] +$array.Add(3) > $null    # $array is now [1, 2, 4, 3] +# Remove from end with index of count of objects-1; array index starts at 0 +$array.RemoveAt($array.Count-1) # => 3 and array is now [1, 2, 4] +# Let's put it back +$array.Add(3) > $null   # array is now [1, 2, 4, 3] again. + +# Access a list like you would any array +$array[0]   # => 1 +# Look at the last element +$array[-1]  # => 3 + +# Looking out of bounds returns nothing +$array[4]  # blank line returned + +# You can look at ranges with slice syntax. +# The start index is included, the end index is not +# (It's a closed/open range for you mathy types.) +$array[1..3]   # Return array from index 1 to 3 => [2, 4] +$array[2..-1]    # Return array starting from index 2 => [4, 3] +$array[0..3]    # Return array from beginning until index 3  => [1, 2, 4] +$array[0..2]   # Return array selecting every second entry => [1, 4] +$array.Reverse()  # mutates array to reverse order => [3, 4, 2, 1] +# Use any combination of these to make advanced slices + +# Remove arbitrary elements from a array with "del" +$array.Remove($array[2])  # $array is now [1, 2, 3] + +# Insert an element at a specific index +$array.Insert(1, 2)  # $array is now [1, 2, 3] again + +# Get the index of the first item found matching the argument +$array.IndexOf(2)  # => 1 +$array.IndexOf(6)  # Returns -1 as "outside array"  + +# You can add arrays +# Note: values for $array and for $otherArray are not modified. +$array + $otherArray  # => [1, 2, 3, 4, 5, 6] + +# Concatenate arrays with "AddRange()" +$array.AddRange($otherArray)  # Now $array is [1, 2, 3, 4, 5, 6] + +# Check for existence in a array with "in" +1 -in $array  # => True + +# Examine length with "Count" (Note: "Length" on arrayList = each items length) +$array.Count  # => 6 + + +# Tuples are like arrays but are immutable. +# To use Tuples in powershell, you must use the .NET tuple class. +$tuple = [System.Tuple]::Create(1, 2, 3) +$tuple.Item(0)      # => 1 +$tuple.Item(0) = 3  # Raises a TypeError + +# You can do some of the array methods on tuples, but they are limited. +$tuple.Length       # => 3 +$tuple + (4, 5, 6)  # => Exception +$tuple[0..2]        # => $null +2 -in $tuple        # => False + + +# Hashtables store mappings from keys to values, similar to Dictionaries. +$emptyHash = @{} +# Here is a prefilled dictionary +$filledHash = @{"one"= 1  +                "two"= 2  +                "three"= 3} + +# Look up values with [] +$filledHash["one"]  # => 1 + +# Get all keys as an iterable with ".Keys". +# items maintain the order at which they are inserted into the dictionary. +$filledHash.Keys  # => ["one", "two", "three"] + +# Get all values as an iterable with ".Values". +$filledHash.Values  # => [1, 2, 3] + +# Check for existence of keys or values in a hash with "-in" +"one" -in $filledHash.Keys  # => True +1 -in $filledHash.Values    # => False + +# Looking up a non-existing key returns $null +$filledHash["four"]  # $null + +# Adding to a dictionary +$filledHash.Add("five",5)  # $filledHash["five"] is set to 5 +$filledHash.Add("five",6)  # exception "Item with key "five" has already been added" +$filledHash["four"] = 4 # $filledHash["four"] is set to 4, running again does nothing + +# Remove keys from a dictionary with del +$filledHash.Remove("one") # Removes the key "one" from filled dict + + +#################################################### +## 3. Control Flow and Iterables +#################################################### + +# Let's just make a variable +$someVar = 5 + +# Here is an if statement. +# This prints "$someVar is smaller than 10" +if ($someVar -gt 10) { +    Write-Output "$someVar is bigger than 10." +} +elseif ($someVar -lt 10) {    # This elseif clause is optional. +    Write-Output "$someVar is smaller than 10." +} +else {                        # This is optional too. +    Write-Output "$someVar is indeed 10." +} + + +<# +Foreach loops iterate over arrays +prints: +    dog is a mammal +    cat is a mammal +    mouse is a mammal +#> +foreach ($animal in ("dog", "cat", "mouse")) { +    # You can use -f to interpolate formatted strings +    "{0} is a mammal" -f $animal +} + +<# +For loops iterate over arrays and you can specify indices +prints: +   0 a +   1 b +   2 c +   3 d +   4 e +   5 f +   6 g +   7 h +#> +$letters = ('a','b','c','d','e','f','g','h') +for($i=0; $i -le $letters.Count-1; $i++){ +    Write-Host $i, $letters[$i] +} + +<# +While loops go until a condition is no longer met. +prints: +    0 +    1 +    2 +    3 +#> +$x = 0 +while ($x -lt 4) { +    Write-Output $x +    $x += 1  # Shorthand for x = x + 1  }  # Switch statements are more powerful compared to most languages @@ -121,88 +392,53 @@ switch($val) {    { $_ -like 's*' }       { "Case insensitive"; break }    { $_ -clike 's*'}       { "clike, ceq, cne for case sensitive"; break }    { $_ -notmatch '^.*$'}  { "Regex matching. cnotmatch, cnotlike, ..."; break } -  { 'x' -contains 'x'}    { "FALSE! -contains is for lists!"; break }    default                 { "Others" }  } -# The classic for -for($i = 1; $i -le 10; $i++) { -  "Loop number $i" +# Handle exceptions with a try/catch block +try { +    # Use "throw" to raise an error +    throw "This is an error"  } -# Or shorter -1..10 | % { "Loop number $_" } - -# PowerShell also offers -foreach ($var in 'val1','val2','val3') { echo $var } -# while () {} -# do {} while () -# do {} until () - -# Exception handling -try {} catch {} finally {} -try {} catch [System.NullReferenceException] { -	echo $_.Exception | Format-List -Force +catch { +    Write-Output $Error.ExceptionMessage +} +finally { +    Write-Output "We can clean up resources here"  } -### Providers -# List files and directories in the current directory -ls # or `dir` -cd ~ # goto home - -Get-Alias ls # -> Get-ChildItem -# Uh!? These cmdlets have generic names because unlike other scripting -# languages, PowerShell does not only operate in the current directory. -cd HKCU: # go to the HKEY_CURRENT_USER registry hive - -# Get all providers in your session -Get-PSProvider - - -### Pipeline -# Cmdlets have parameters that control their execution: -Get-ChildItem -Filter *.txt -Name # Get just the name of all txt files -# Only need to type as much of a parameter name until it is no longer ambiguous -ls -fi *.txt -n # -f is not possible because -Force also exists -# Use `Get-Help Get-ChildItem -Full` for a complete overview - -# Results of the previous cmdlet can be passed to the next as input. -# `$_` is the current object in the pipeline object. -ls | Where-Object { $_.Name -match 'c' } | Export-CSV export.txt -ls | ? { $_.Name -match 'c' } | ConvertTo-HTML | Out-File export.html +# Writing to a file +$contents = @{"aa"= 12  +             "bb"= 21} +$contents | Export-CSV "$env:HOMEDRIVE\file.csv" # writes to a file -# If you get confused in the pipeline use `Get-Member` for an overview -# of the available methods and properties of the pipelined objects: -ls | Get-Member -Get-Date | gm +$contents = "test string here" +$contents | Out-File "$env:HOMEDRIVE\file.txt" # writes to another file -# ` is the line continuation character. Or end the line with a | -Get-Process | Sort-Object ID -Descending | Select-Object -First 10 Name,ID,VM ` -	| Stop-Process -WhatIf +# Read file contents and convert to json +Get-Content "$env:HOMEDRIVE\file.csv" | ConvertTo-Json -Get-EventLog Application -After (Get-Date).AddHours(-2) | Format-List -# Use % as a shorthand for ForEach-Object -(a,b,c) | ForEach-Object ` -	-Begin { "Starting"; $counter = 0 } ` -	-Process { "Processing $_"; $counter++ } ` -	-End { "Finishing: $counter" } +#################################################### +## 4. Functions +#################################################### -# Get-Process as a table with three columns -# The third column is the value of the VM property in MB and 2 decimal places -# Computed columns can be written more verbose as: -# `@{name='lbl';expression={$_}` -ps | Format-Table ID,Name,@{n='VM(MB)';e={'{0:n2}' -f ($_.VM / 1MB)}} -autoSize +# Use "function" to create new functions +# Keep the Verb-Noun naming convention for functions +function Add-Numbers { + $args[0] + $args[1] +} +Add-Numbers 1 2 # => 3 -### Functions -# The [string] attribute is optional. -function foo([string]$name) { -	echo "Hey $name, have a function" +# Calling functions with parameters +function Add-ParamNumbers { + param( [int]$firstNumber, [int]$secondNumber ) + $firstNumber + $secondNumber  } -# Calling your function -foo "Say my name" +Add-ParamNumbers -FirstNumber 1 -SecondNumber 2 # => 3   # Functions with named parameters, parameter attributes, parsable documentation  <# @@ -219,112 +455,353 @@ New-Website siteName 2000 # ERROR! Port argument could not be validated  ('name1','name2') | New-Website -Verbose  #>  function New-Website() { -	[CmdletBinding()] -	param ( -		[Parameter(ValueFromPipeline=$true, Mandatory=$true)] -		[Alias('name')] -		[string]$siteName, -		[ValidateSet(3000,5000,8000)] -		[int]$port = 3000 -	) -	BEGIN { Write-Verbose 'Creating new website(s)' } -	PROCESS { echo "name: $siteName, port: $port" } -	END { Write-Verbose 'Website(s) created' } +    [CmdletBinding()] +    param ( +        [Parameter(ValueFromPipeline=$true, Mandatory=$true)] +        [Alias('name')] +        [string]$siteName, +        [ValidateSet(3000,5000,8000)] +        [int]$port = 3000 +    ) +    BEGIN { Write-Output 'Creating new website(s)' } +    PROCESS { Write-Output "name: $siteName, port: $port" } +    END { Write-Output 'Website(s) created' }  } -### It's all .NET -# A PS string is in fact a .NET System.String -# All .NET methods and properties are thus available -'string'.ToUpper().Replace('G', 'ggg') -# Or more powershellish -'string'.ToUpper() -replace 'G', 'ggg' - -# Unsure how that .NET method is called again? -'string' | gm - -# Syntax for calling static .NET methods -[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') - -# Note that .NET functions MUST be called with parentheses -# while PS functions CANNOT be called with parentheses. -# If you do call a cmdlet/PS function with parentheses, -# it is the same as passing a single parameter list -$writer = New-Object System.IO.StreamWriter($path, $true) -$writer.Write([Environment]::NewLine) -$writer.Dispose() - -### IO -# Reading a value from input: -$Name = Read-Host "What's your name?" -echo "Hello, $Name!" -[int]$Age = Read-Host "What's your age?" - -# Test-Path, Split-Path, Join-Path, Resolve-Path -# Get-Content filename # returns a string[] -# Set-Content, Add-Content, Clear-Content -Get-Command ConvertTo-*,ConvertFrom-* - - -### Useful stuff -# Refresh your PATH -$env:PATH = [System.Environment]::GetEnvironmentVariable("Path", "Machine") +  -	";" + [System.Environment]::GetEnvironmentVariable("Path", "User") - -# Find Python in path -$env:PATH.Split(";") | Where-Object { $_ -like "*python*"} - -# Change working directory without having to remember previous path -Push-Location c:\temp # change working directory to c:\temp -Pop-Location # change back to previous working directory -# Aliases are: pushd and popd - -# Unblock a directory after download -Get-ChildItem -Recurse | Unblock-File - -# Open Windows Explorer in working directory -ii . - -# Any key to exit -$host.UI.RawUI.ReadKey() -return - -# Create a shortcut -$WshShell = New-Object -comObject WScript.Shell -$Shortcut = $WshShell.CreateShortcut($link) -$Shortcut.TargetPath = $file -$Shortcut.WorkingDirectory = Split-Path $file -$Shortcut.Save() +#################################################### +## 5. Modules +#################################################### + +# You can import modules and install modules +# The Install-Module is similar to pip or npm, pulls from Powershell Gallery +Install-Module dbaTools +Import-Module dbaTools + +$query = "SELECT * FROM dbo.sometable" +$queryParams = @{ +    SqlInstance = 'testInstance' +    Database    = 'testDatabase' +    Query       = $query +} +Invoke-DbaQuery @queryParams + +# You can get specific functions from a module +Import-Module -Function Invoke-DbaQuery + + +# Powershell modules are just ordinary Posh files. You +# can write your own, and import them. The name of the +# module is the same as the name of the file. + +# You can find out which functions and attributes +# are defined in a module. +Get-Command -module dbaTools +Get-Help dbaTools -Full + + +#################################################### +## 6. Classes +#################################################### + +# We use the "class" statement to create a class +class Instrument { +    [string]$Type +    [string]$Family +} + +$instrument = [Instrument]::new() +$instrument.Type = "String Instrument" +$instrument.Family = "Plucked String" + +$instrument + +<# Output: +Type              Family         +----              ------         +String Instrument Plucked String +#> + + +#################################################### +## 6.1 Inheritance +#################################################### + +# Inheritance allows new child classes to be defined that inherit  +# methods and variables from their parent class. + +class Guitar : Instrument +{ +    [string]$Brand +    [string]$SubType +    [string]$ModelType +    [string]$ModelNumber +} + +$myGuitar = [Guitar]::new() +$myGuitar.Brand       = "Taylor" +$myGuitar.SubType     = "Acoustic" +$myGuitar.ModelType   = "Presentation" +$myGuitar.ModelNumber = "PS14ce Blackwood" + +$myGuitar.GetType() + +<# +IsPublic IsSerial Name                                     BaseType                                                +-------- -------- ----                                     --------                                                +True     False    Guitar                                   Instrument   +#> + + +#################################################### +## 7. Advanced +#################################################### + +# The powershell pipeline allows things like High-Order Functions. + +# Group-Object is a handy cmdlet that does incredible things. +# It works much like a GROUP BY in SQL. + +<# + The following will get all the running processes, + group them by Name, + and tell us how many instances of each process we have running. + Tip: Chrome and svcHost are usually big numbers in this regard. +#> +Get-Process | Foreach-Object ProcessName | Group-Object + +# Useful pipeline examples are iteration and filtering. +1..10 | ForEach-Object { "Loop number $PSITEM" } +1..10 | Where-Object { $PSITEM -gt 5 } | ConvertTo-Json + +# A notable pitfall of the pipeline is it's performance when +# compared with other options. +# Additionally, raw bytes are not passed through the pipeline, +# so passing an image causes some issues. +# See more on that in the link at the bottom. + +<# + Asynchronous functions exist in the form of jobs. + Typically a procedural language, + Powershell can operate non-blocking functions when invoked as Jobs. +#> + +# This function is known to be non-optimized, and therefore slow. +$installedApps = Get-CimInstance -ClassName Win32_Product + +# If we had a script, it would hang at this func for a period of time. +$scriptBlock = {Get-CimInstance -ClassName Win32_Product} +Start-Job -ScriptBlock $scriptBlock + +# This will start a background job that runs the command. +# You can then obtain the status of jobs and their returned results. +$allJobs = Get-Job +$jobResponse = Get-Job | Receive-Job + + +# Math is built in to powershell and has many functions. +$r=2 +$pi=[math]::pi +$r2=[math]::pow( $r, 2 ) +$area = $pi*$r2 +$area + +# To see all possibilities, check the members. +[System.Math] | Get-Member -Static -MemberType All + + +<# + This is a silly one: + You may one day be asked to create a func that could take $start and $end + and reverse anything in an array within the given range + based on an arbitrary array without mutating the original array. + Let's see one way to do that and introduce another data structure. +#> + +$targetArray = 'a','b','c','d','e','f','g','h','i','j','k','l','m' + +function Format-Range ($start, $end, $array) { +    [System.Collections.ArrayList]$firstSectionArray = @() +    [System.Collections.ArrayList]$secondSectionArray = @() +    [System.Collections.Stack]$stack = @() +    for ($index = 0; $index -lt $array.Count; $index++) { +        if ($index -lt $start) { +            $firstSectionArray.Add($array[$index]) > $null +        } +        elseif ($index -ge $start -and $index -le $end) { +            $stack.Push($array[$index]) +        } +        else { +            $secondSectionArray.Add($array[$index]) > $null +        } +    } +    $finalArray = $firstSectionArray + $stack.ToArray() + $secondSectionArray +    return $finalArray +} + +Format-Range 2 6 $targetArray  +# => 'a','b','g','f','e','d','c','h','i','j','k','l','m' + +# The previous method works, but uses extra memory by allocating new arrays. +# It's also kind of lengthy. +# Let's see how we can do this without allocating a new array. +# This is slightly faster as well. + +function Format-Range ($start, $end) { +  while ($start -lt $end) +  { +      $temp = $targetArray[$start] +      $targetArray[$start] = $targetArray[$end] +      $targetArray[$end] = $temp +      $start++ +      $end-- +  } +  return $targetArray +} + +Format-Range 2 6 # => 'a','b','g','f','e','d','c','h','i','j','k','l','m' +``` +Powershell as a Tool: + +Getting Help: + +```Powershell +# Find commands +Get-Command about_* # alias: gcm +Get-Command -Verb Add +Get-Alias ps +Get-Alias -Definition Get-Process + +Get-Help ps | less # alias: help +ps | Get-Member # alias: gm + +Show-Command Get-WinEvent # Display GUI to fill in the parameters + +Update-Help # Run as admin  ``` +If you are uncertain about your environment: -Configuring your shell +```Powershell +Get-ExecutionPolicy -List +Set-ExecutionPolicy AllSigned +# Execution policies include: +# - Restricted: Scripts won't run. +# - RemoteSigned: Downloaded scripts run only if signed by a trusted publisher.  +# - AllSigned: Scripts need to be signed by a trusted publisher. +# - Unrestricted: Run all scripts. +help about_Execution_Policies # for more info +# Current PowerShell version: +$PSVersionTable  ``` -# $Profile is the full path for your `Microsoft.PowerShell_profile.ps1` -# All code there will be executed when the PS session starts -if (-not (Test-Path $Profile)) { -	New-Item -Type file -Path $Profile -Force -	notepad $Profile + +```Powershell +# Calling external commands, executables,  +# and functions with the call operator. +# Exe paths with arguments passed or containing spaces can create issues. +C:\Program Files\dotnet\dotnet.exe +# The term 'C:\Program' is not recognized as a name of a cmdlet, +# function, script file, or executable program. +# Check the spelling of the name, or if a path was included,  +# verify that the path is correct and try again + +"C:\Program Files\dotnet\dotnet.exe" +C:\Program Files\dotnet\dotnet.exe    # returns string rather than execute + +&"C:\Program Files\dotnet\dotnet.exe --help"   # fail +&"C:\Program Files\dotnet\dotnet.exe" --help   # success +# Alternatively, you can use dot-sourcing here +."C:\Program Files\dotnet\dotnet.exe" --help   # success + +# the call operator (&) is similar to Invoke-Expression,  +# but IEX runs in current scope. +# One usage of '&' would be to invoke a scriptblock inside of your script. +# Notice the variables are scoped +$i = 2 +$scriptBlock = { $i=5; Write-Output $i } +& $scriptBlock # => 5 +$i # => 2 + +invoke-expression ' $i=5; Write-Output $i ' # => 5 +$i # => 5 + +# Alternatively, to preserve changes to public variables +# you can use "Dot-Sourcing". This will run in the current scope. +$x=1 +&{$x=2};$x # => 1 + +.{$x=2};$x # => 2 + + +# Remoting into computers is easy. +Enter-PSSession -ComputerName RemoteComputer + +# Once remoted in, you can run commands as if you're local. +RemoteComputer\PS> Get-Process powershell + +<# +Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName                                              +-------  ------    -----      -----     ------     --  -- -----------                                              +   1096      44   156324     179068      29.92  11772   1 powershell                                               +    545      25    49512      49852             25348   0 powershell  +#> +RemoteComputer\PS> Exit-PSSession + +<# + Powershell is an incredible tool for Windows management and Automation. + Let's take the following scenario: + You have 10 servers. + You need to check whether a service is running on all of them. + You can RDP and log in, or PSSession to all of them, but why? + Check out the following +#> + +$serverList = @( +    'server1', +    'server2', +    'server3', +    'server4', +    'server5', +    'server6', +    'server7', +    'server8', +    'server9', +    'server10' +) + +[scriptblock]$script = { +    Get-Service -DisplayName 'Task Scheduler' +} + +foreach ($server in $serverList) { +    $cmdSplat = @{ +        ComputerName  = $server +        JobName       = 'checkService' +        ScriptBlock   = $script +        AsJob         = $true +        ErrorAction   = 'SilentlyContinue' +    } +    Invoke-Command @cmdSplat | Out-Null  } -# More info: `help about_profiles` -# For a more useful shell, be sure to check the project PSReadLine below + +<# + Here we've invoked jobs across many servers. + We can now Receive-Job and see if they're all running. + Now scale this up 100x as many servers :) +#>  ```  Interesting Projects    * [Channel9](https://channel9.msdn.com/Search?term=powershell%20pipeline#ch9Search&lang-en=en) PowerShell tutorials +* [KevinMarquette's Powershell Blog](https://powershellexplained.com/) Excellent blog that goes into great detail on Powershell  * [PSGet](https://github.com/psget/psget) NuGet for PowerShell  * [PSReadLine](https://github.com/lzybkr/PSReadLine/) A bash inspired readline implementation for PowerShell (So good that it now ships with Windows10 by default!)  * [Posh-Git](https://github.com/dahlbyk/posh-git/) Fancy Git Prompt (Recommended!) +* [Oh-My-Posh](https://github.com/JanDeDobbeleer/oh-my-posh) Shell customization similar to the popular Oh-My-Zsh on Mac  * [PSake](https://github.com/psake/psake) Build automation tool  * [Pester](https://github.com/pester/Pester) BDD Testing Framework  * [Jump-Location](https://github.com/tkellogg/Jump-Location) Powershell `cd` that reads your mind -* [PowerShell Community Extensions](http://pscx.codeplex.com/) (Dead) - -Not covered   - -* WMI: Windows Management Intrumentation (Get-CimInstance)   -* Multitasking: Start-Job -scriptBlock {...},  -* Code Signing -* Remoting (Enter-PSSession/Exit-PSSession; Invoke-Command) +* [PowerShell Community Extensions](https://github.com/Pscx/Pscx) +* [More on the Powershell Pipeline Issue](https://github.com/PowerShell/PowerShell/issues/1908) | 
