summaryrefslogtreecommitdiffhomepage
path: root/powershell.html.markdown
diff options
context:
space:
mode:
Diffstat (limited to 'powershell.html.markdown')
-rw-r--r--powershell.html.markdown962
1 files changed, 719 insertions, 243 deletions
diff --git a/powershell.html.markdown b/powershell.html.markdown
index db29bf96..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,105 +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
+#>
-```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
+####################################################
+## 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
-```powershell
-# 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
-$anEmptyList = @()
-$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
@@ -122,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
<#
@@ -220,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
-Configuring your shell
+Get-Help ps | less # alias: help
+ps | Get-Member # alias: gm
-```powershell
-# $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
+Show-Command Get-WinEvent # Display GUI to fill in the parameters
+
+Update-Help # Run as admin
+```
+
+If you are uncertain about your environment:
+
+```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
+```
+
+```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)