About Powershell
Topic |
Summary |
Office365AdminWithPowershell |
Managing Office365 from Powershell For all cases below you need to set the executionpolicy Once for every PC you need to run this and select A for ALL Set Execut... |
PowerShell |
About Powershell * See also https://ss64.com/ps/ * See also these pages: %SEARCH{" pP ower sS hell" type="regex" scope="topic" nonoise="on" header=" Topi... |
PowerShellManagingPCsRemotely |
Managing PCs With Remote PowerShell Powershell remoting (Traditional without SSH) This is perfect for windows domains but irritating and MAYBE unsecure for workg... |
PowerShellScripts |
Usefull powershell scripts function to sum durations found in text and report total minutes function sum_durations_from_text($text) { # read multiline te... |
PowerShell as a shell
A sensible configuration for your shell (for a good UX)
Download
less
because PowerShell's
more
is a pain to use:
https://sourceforge.net/projects/gnuwin32/files/less/
Add a few options to your profile (for the curious read the github link):
if (Test-Path $Profile) {} else {New-Item –Path $Profile –Type File –Force}
echo "# Enable PSReadLine -- https://github.com/PowerShell/PSReadLine/blob/master/README.md" >> $profile
echo "Import-Module PSReadLine" >> $profile
echo "# Same shortcut keys with Linux" >> $profile
echo "Set-PSReadLineOption -EditMode Emacs" >> $profile
Linux geeks can enjoy
less, grep and
vim and even
:grep inside vim by
1) installing git
2) setting up their $Profile like so:
# Enable PSReadLine
# https://github.com/PowerShell/PSReadLine/blob/master/README.md
Import-Module PSReadLine
# Same shortcut keys with Linux
Set-PSReadLineOption -EditMode Emacs
sal less "C:\Program Files\Git\usr\bin\less.exe"
sal grep "C:\Program Files\Git\usr\bin\grep.exe"
sal vim "C:\Program Files\Git\usr\bin\vim.exe"
sal vi "C:\Program Files\Git\usr\bin\vim.exe"
3) Finally, start vim, type
:e $MYVIMRC to edit your vim config and add this line
set shell=/bin/bash to enjoy
:%!grep ...
Shell absolute basics
# executing programs outside the current dir
& 'c:\path\to\program\with spaces\program.exe'
# executing programs in the current dir
.\prog_name_wo_spaces.exe
& '.\program name with spaces.exe'
help Get-Process # Help about a command
help *process* # List commands containing process in their name
Show-Command Some-Complex-Cmd # graphically prompts you for the command’s parameters.
Get-Process -name Notepad | Stop-Process # cmdlets with the same noun can often pass information among each other
Exploring the output of command and producing useful output from commands
Get-Process | gm # list properties and methods (gm=get-methods)
Get-Process | select * # display all properties and their values
Get-Process | ogv # view output on an Excel like GUI *very* handy for long lists or lists with large texts (ogv=out-GridView)
Get-Process | Format-Table -autoSize # force the shell to try to size each column to hold its contents and no more
Get-Service | Format-Table -autoSize -wrap # -wrap will never truncate text with ...
Get-Process | Format-Table -property ID,Name,Responding -autoSize # specific properties only
Get-Process | Format-List # like Format-Table but vertical (one line per property)
Get-Process | Select * # show all properties (by default some are hiden)
Get-Process | Format-List * # -->>--
Get-Process | Sort -Property VM | Select Name,ID,VM,PM # sort & display specific properties
Get-Process | Sort -Property VM | Select Name,ID,VM,PM -First 10 # only the first 10 objects
Get-Process | Format-List | Out-String | ForEach-Object { $_.Trim() } # after Out-String the output is a long string, we use ForEach-Object to remove empty lines
-OR-
... | Out-String -Stream | sls "word"
-OR- ... | Out-String -Stream | where {$_ -notmatch ': *$|: {}$'}
$FormatEnumerationLimit =-1
# makes format-table to print full property values instead of 16 characters with ...
Enumerating Arrays/Streams of objects
# ENUMERATING ONLY (all these do the same thing)
ForEach ($file in (Get-ChildItem)) {$file.fullname}
Get-ChildItem | ForEach-Object { $_.fullname }
Get-ChildItem | ForEach { $_.fullname }
Get-ChildItem | %{ $_.fullname }
Get-ChildItem | ForEach fullname
# FILTERING (all these do the same thing)
Get-ChildItem | Where-Object { $_.fullname -match '[.]docx$' }
Get-ChildItem | Where { $_.fullname -match '[.]docx$' }
Get-ChildItem | ? { $_.fullname -match '[.]docx$' }
# you can also use these operators -notmatch, -eq, -ne, -lt, -gt, -le, -ge, -like, -notlike
# ALL TOGETHER Enumerate -> filter -> map
Get-ChildItem | Where { $_.fullname -match '[.]docx$' } | ForEach { [PSCustomObject]@{FileName=$_.Name; FileSize=$_.Length} }
# How to split a text in lines
$array_of_lines = ($text -split "`r?`n") # '`n' does not work, and note the trick with `r?
More Advanced
Get-Service | Sort Status | Format-Table -groupBy Status # sorting first matters
Get-Process | Sort -Property VM | Select Name,ID,VM,PM | ConvertTo-HTML > processes.html # or json or csv
Get-Process | Format-Table Name, @{name='VM(MB)';expression={$_.VM / 1MB -as [int]}} -autosize # calculated field VM(MB)
Get-Process | Where { $_.Name -notlike 'powershell*' } | Measure-Object -property VM -sum # SUM of a column(property)
Even more Advanced: Parenthetical commands
# -computername gets a list of computer names from c:\names.txt filtered
Get-Service -computername (Get-Content c:\names.txt | Where-Object -filter { $_ -notlike '*dc' })
# It's the same as these two commands:
$list = (Get-Content c:\names.txt | Where-Object -filter { $_ -notlike '*dc' })
Get-Service -computername $list
Dealing with Greek (Character encodings)
chcp 1252 # change character encoding to Windows-Greek (ISE is by default set to UTF8)
Get-Content -Raw -Encoding default file.log > temp.txt; sls 'ελληνικά' temp.txt # how to use sls on a file with ANSI(cp1253) encoding
Powershell <--> Linux cheat-sheet
cat log.txt | sls '^[a-z]' # grep
cat sourcefile.txt | ?{$_ -notmatch 'NotThis'} # grep -v
gc log.txt | select -first 10 # head
gc log.txt | select -last 10 # tail
gc log.txt | more # or less if you have it installed
echo test | %{ $_ -replace 'e', 'o' } # sed
ls 2> $null # /dev/null is $null in P.S.
gc -TotalCount 10 log.txt # also head
gc -Tail 10 log.txt # also tail -- much faster than above option
(cat $file.FullName | Measure-Object -line).lines # wc -l
cat lala | group | sort # cat lala | sort | uniq -c
(Get-Process | Measure-Object).count # almost what wc -l would do -- it counts how many objects Get-Process emmits
(get-date) - (gcim Win32_OperatingSystem).LastBootUpTime
# uptime (also see systeminfo | sls "Boot"
Set-PSDebug -Trace 1; script.ps1 # bash -x script.sh
Scripting help
Things to keep in mind
ALWAYS and I mean ALWAYS start your scripts with this line that raises an error if you use an undeclared/unassigned variable:
Set-strictmode -version latest
If you want execution of a script to stop on the first error (which you want if you are not very thoughtful about every line of code you write):
$ErrorActionPreference="Stop"
Note that the above will catch most errors but may miss errors when calling external programs (or DOS commands). See
https://stackoverflow.com/a/11450852/1011025
How to debug
Set-PSBreakpoint "script.ps1" 123 # commands: Step into, step oVer, step Out, Continue, quit
Get-PSBreakpoint| Remove-PSBreakpoint
Set-PSBreakpoint -Variable StackTrace -Mode Write #Break on any error
Get-PSBreakpoint | Remove-PSBreakpoint
Common Gotchas
THIS IS WRONG! THIS IS CORRECT!
Start-Sleet 60*60 Start-Sleep (60*60)
echo "$_.name" echo "$($_.name)"
PowerShell Reference
# Single line comments start with a number symbol.
<#
Multi-line comments
like so
#>
####################################################
## 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'
}
}
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
####################################################
## 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
# 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
$val = "20"
switch($val) {
{ $_ -eq 42 } { "The answer equals 42"; break }
'20' { "Exactly 20"; break }
{ $_ -like 's*' } { "Case insensitive"; break }
{ $_ -clike 's*'} { "clike, ceq, cne for case sensitive"; break }
{ $_ -notmatch '^.*$'} { "Regex matching. cnotmatch, cnotlike, ..."; break }
default { "Others" }
}
# Handle exceptions with a try/catch block
try {
# Use "throw" to raise an error
throw "This is an error"
}
catch {
Write-Output $Error.ExceptionMessage
}
finally {
Write-Output "We can clean up resources here"
}
# Writing to a file
$contents = @{"aa"= 12
"bb"= 21}
$contents | Export-CSV "$env:HOMEDRIVE\file.csv" # writes to a file
$contents = "test string here"
$contents | Out-File "$env:HOMEDRIVE\file.txt" # writes to another file
# Read file contents and convert to json
Get-Content "$env:HOMEDRIVE\file.csv" | ConvertTo-Json
####################################################
## 4. Functions
####################################################
# 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
# Calling functions with parameters
function Add-ParamNumbers {
param( [int]$firstNumber, [int]$secondNumber )
$firstNumber + $secondNumber
}
Add-ParamNumbers -FirstNumber 1 -SecondNumber 2 # => 3
# Functions with named parameters, parameter attributes, parsable documentation
<#
.SYNOPSIS
Setup a new website
.DESCRIPTION
Creates everything your new website needs for much win
.PARAMETER siteName
The name for the new website
.EXAMPLE
New-Website -Name FancySite -Po 5000
New-Website SiteWithDefaultPort
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-Output 'Creating new website(s)' }
PROCESS { Write-Output "name: $siteName, port: $port" }
END { Write-Output 'Website(s) created' }
}
####################################################
## 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
#Comment Comment
<# A comment spanning
more than one
lines #>
$x = "Bruce `"The Boss`" Springsteen" # to include " inside " we use `
`t Tab
`n New line
` at end of line means the command continues to the next line
Flow Control
If(){} Elseif(){ } Else{ }
while(){}
For($i=0; $i -lt 10; $i++){}
Foreach($file in dir C:\){$file.name}
1..10 | foreach{$_}
Operators
-and -or -not (you can use
!
instead of
-not
)
-eq -ne
Equal, not equal
-match -notmatch Regular expression match
-like -notlike
Wildcard matching
-contains,
-notcontains Check if value is in array
-in -notin
Reverse of contains,notcontains.
-gt -ge Greater than, greater than or equal
-lt -le Less than, less than or equal
$string -replace 'this','that'
--OR-- $string -replace '(.*), (.*)','$2,$1'
You can use
+
-= *= /= %= ++ --=
Variables
$var = "string" Assign variable
$a,$b = 0 or $a,$b = 'a','b' Assign multiple variables
$a,$b = $b,$a Flip variables
$var=[int]5 Strongly typed variable
[string]::IsNullOrEmpty($Service.Description) # Check if variable is null or empty
[string]::IsNullOrWhiteSpace($var) # Check if variable is null or contains whitespace only
# test if a variable exists
Test-Path variable:global:var1
Test-Path variable:script:var1
Test-Path variable:var1
Arrays
@(Get-Process) Forces the result to an array using the array subexpression operator
$arr = "a", "b", "c" Array of strings
$arr.length # length of array
$arr = 1,2,3 # Array of integers
$arr = @() #Empty array
$arr = @(2) # Array of one element
$arr = $arr + @("new") # append one element to array
$arr = 1,(2,3),4 Array within array
$arr[0] First array element
$arr[-1] last array element
$arr[0..2] first 3 elements
$arr[-3..-1] last three elements
$arr[1,4+6..9] Displays the elements at index positions 1,4, and 6 through 9
$arr=1..10
$arr[($arr.length-1)..0] Reverses an array
$arr[1] += 200 Adds to an existing value of the second array item (increases the value of the element)
$z = $arr + $b Combines two arrays into a single array
Associative Arrays (Hash tables)
$hash = @{} # Creates empty hash table$hash = @{foo=1; bar='value2'} # Creates and initialize a hash table
[ordered]@{a=1; b=2; c=3} # Creates an ordered dictionary$hash.key1 = 1; $hash["key1"] = 1 # Assigns 1 to key key1$hash.key1 ; $hash['key1'] # Returns value of key1$hash.GetEnumerator() | sort Key # Sorts a hash table by the Key property
Create Custome Objects on the fly
[PSCustomObject]@{x=1; y=2} # converts dict to PSCustomObject
$obj=@{x=1; y=2}; [PSCustomObject]@obj # -->>--
# example
Get-ChildItem | %{ [PSCustomObject]@{FileName=$_.Name; FileSize=$_.Length} }
Object Properties
(Get-Date).Date Date property of object
$a = Get-Date
$a | Get-Member –MemberType Property
$a.Date
$a.TimeOfDay.Hours
$a | Get-Member -MemberType Property –Static
Static properties can be referenced with the "::" operator.
[DateTime]::Now
Methods
Methods can be called on objects.
$a = "This is a string"
$a | Get-Member –MemberType Method
$a.ToUpper(), $a.ToLower() # convert to upper case, lower case
$a.Substring(0,3)
$a | Get-Member -MemberType Method -Static
Static methods are callable with the "::" operator.
[DateTime]::IsLeapYear(2012)
Strings
"This is a string, this $variable is expanded as is $($var.property[4]+2) and ${variable}"
‘This is a string, this $variable is not expanded"
@"
This is a here-string which can contain anything including carriage returns and quotes. Expressions are evaluated: $(2+2*5). Note that the end marker of the here-string must be at the beginning of a line!
"@
@'
Here-strings with single quotes do not evaluate expressions:
$(2+2*5)
'@
Functions
PS C:\> function test1($x,$y) {echo "$x $y"}
PS C:\> function test2([int]$x, [int]$y) {echo "$x $y"}
PS C:\> test1 1, 2
1 2
PS C:\> test2 -x 1 -y 2
1 2
PS C:\> $v=(test2 -x 1 -y 2 )
PS C:\> $v
1 2
PS C:\> $v=(test1 1 2 )
PS C:\> $v
1 2
Convert if to a function (inline if/iif)
Enclose the if statement in
(&{ ...
}) like so:
echo "Condition is " +
(&{If($Condition) {"True"} Else {"False"}
})
Variables
Format: $[scope:]name or ${anyname} or ${any path}
$path = "C:\Windows\System32"
Get-ChildItem ${env:ProgramFiles(x86)} $processes = Get-Process
$global:a
1 # visible everywhere
$local:a = 1 # defined in this scope and visible to children
$private:a = 1 # same as local but invisible to child scopes
$script:a = 1 # visible to everything is this script # Using scope indicates a local variable in remote commands and with Start-Job $localVar = Read-Host "Directory, please" Invoke-Command -ComputerName localhost -ScriptBlock {
dir $using:localVar } Start-Job { dir $using:localVar -Recurse}
$env:Path +
";D:\Scripts"
Get-Command -Noun Variable # the Variable Cmdlets Get-ChildItem variable: # listing all variables using the variable drive
# strongly-typed variable (can contain only integers) [int]$number=8
# attributes can be used on variables
[ValidateRange(1,10)][int]$number = 1
$number = 11 #returns an error
# flip variables
$a=1;$b=2
$a,$b = $b,$a
# multi assignment $a,$b,$c = 0
$a,$b,$c = 'a','b','c'
$a,$b,$c = 'a b c'.split()
# create read only variable (can be overwritten with -Force)
Set-Variable -Name ReadOnlyVar -Value 3 -Option ReadOnly
# create Constant variable (cannot be overwritten)
Set-Variable -Name Pi -Value 3.14 -Option Constant
PowerShellAutomatic Variables (not exhaustive)
$$ Last token of the previous command line
$? Boolean status of last command
$^ First token of the previous command line
$_, $PSItem Current pipeline object
$Args Arguments to a script or function
$Error Array of errors from previous commands (use $error.clear() to clear it)
$ForEach Reference to the enumerator in a foreach loop
$Home The user’s home directory
$Host Reference to the application hosting the POWERSHELL language
$Input Enumerator of objects piped to a script
$LastExitCode Exit code of last program or script
$Matches Exit code of last program or script
$MyInvocation An object with information about the current command
$PSHome The installation location of Windows PowerShell
$profile The standard profile (may not be present)
$Switch Enumerator in a switch statement
$True Boolean value for TRUE
$False Boolean value for FALSE
$PSCulture Current culture
$PSUICulture Current UI culture
$PsVersionTable Details about the version of Windows PowerShell
$Pwd The full path of the current directory
Preference Variables
$ConfirmPreference Determines whether PowerShellautomatically prompts you for confirmation before running a cmdlet or function
$DebugPreference Determines how PowerShellresponds to debugging
$ErrorActionPreference Determines how PowerShellresponds to a nonterminating error
$ErrorView Determines the display format of error messages
$FormatEnumerationLimitDetermines how many enumerated items are included in a display
$MaximumHistoryCount Determines how many commands are saved in the command history for the current session
$OFS Output Field Separator. Specifies the character that separates the elements of an array when the array is converted to a string. The default value is: Space.
$OutputEncoding Determines the character encoding method that PowerShelluses when it sends text to other applications
$PSDefaultParameterValues Specifies default values for the parameters of cmdlets and advanced functions
$PSEmailServer Specifies the default e-mail server that is used to send e-mail messages
$PSModuleAutoLoadingPreference Enables and disables
automatic importing of modules in the session. "All" is the default.
$PSSessionApplicationName Specifies the default application name for a remote command that uses WS-Management technology
$PSSessionConfigurationName Specifies the default session configuration that is used for PSSessions created in the current session
$PSSessionOption Establishes the default values for advanced user options in a remote session
$VerbosePreference Determines how PowerShellresponds to verbose messages generated by a script, cmdlet or provider. Example:
$VerbosePreference = "Continue"; Write-Verbose -Message "Verbose message test." ; $VerbosePreference = "SilentlyContinue"; Write-Verbose -Message "Verbose message test."
$WarningPreference Determines how PowerShellresponds to warning messages generated by a script, cmdlet or provider
$WhatIfPreference Determines whether WhatIf is automatically enabled for every command that supports it
Other Tips
# access environment variable windir (same as %WINDIR% in DOS and $PATH in linux)
echo $env:USERPROFILE $env:USERNAME $env:USERDOMAIN $env:COMPUTERNAME $Env:WINDIR
$temp_file = "$Env:TEMP\$(Get-Random).tmp" # a temp file in appdata\local\temp
Invoke-WebRequest http://www.foo.com/test.zip -OutFile test1.zip # download file
Expand-Archive .\test1.zip # extract files from zip file
# converting between different types
[char]65 # --> "A" converts int to character
[int][char]"A" # --> 65 converts string to char and then the char to int
$time = (Get-Date).ToString("yyyy-MM-dd HH:mm") # convert date to formatted string
[DateTime]"2020-7-16" # this will work
[datetime]::parseexact('01-Jul-16', 'dd-MMM-yy', $null) # but prefere this to avoid surprises (e.g. 7/5/2020 is 7 a day or a month?)
Useful commands in scripts
Write-Host "foo" # writes to console
Write-output "foo" # writes to std-out (which may be the console and maybe not)
start-sleep -s 10 # sleep for 10 seconds
About branches/loops/functions
if (x -eq "foo") {...} else {...} # or -ne -gt -lt -match -notmatch
ForEach ($item in ($command)) {$item ... [break] ...} # loop over the output objects of command. You can use break if you want
command | ForEach-Object {$_ ...} # loop over the output objects of command, $_ is the object CAUTION: you can't and shouldn't use break in it
For ($i=1; $i -le 20; $i++) { ... } # loop 20 times (from $i=1 to $i=20)
Do { ... } While ($i -gt 10)
Do { ... } Until ($i -eq 10)
function some_name { ... }
Useful tips
$out = (ls c:\ | select-string "foo")
echo "$out" # This gives one line
echo $out # BUT this gives MULTIPLE lines
echo "$x$y" # almost like string concatanation but doesn't preserve any new lines in $x,$y
echo ($x + $y) # True string concatanation (preserves new lines in $x,$y)
$out = %{ $out -replace 'foo', 'bar' }"# printing
Get-Date -format yyyy-MM-dd` HH:mm:ss # a nicely formated timestamp
Get-Date -format yyyy-MM-dd_HH.mm.ss # a timestamp formated for filenames
# string formating
$msg = "{0,10} {1}" -f $var0, $var1 # $var0 is left-padded at 10chars
# get a list of all files like Auto*.txt that have been written during the last 8 days
$list = Get-ChildItem -Filter "Auto*.txt" -Force -File -Path 'G:\Soft1 Backup' -ErrorAction SilentlyContinue | ? { $_.LastWriteTime -gt (Get-Date).AddDays(-8) }
# to delete them
$list | del
# to keep only their Full Name:
$list | Select-Object FullName
# to execute commands for each one of the files
foreach ($file in $list) {
...
}
if ($list.length -lt 7) {echo "WARNING! less than 7 backup reports"}
To capture the output of a block of commands
Start-Transcript -Path 'c:\output.txt'
...commands...
Stop-Transcript
Error handling (try catch)
try {
command -ErrorAction Stop
} catch {
echo "command raised error", $error[0]
}
# for non terminating error (MOST ERRORS) you use 2>$null to silence them
# and then iterate over the $error items and to do what you want.
# e.g. here we just print a one line terse error
$error.clear()
remove-item "c:\test1" 2>$null
$error | %{ write-host -foregroundColor red $_.ToString()}
How to catch and handle SPECIFIC errors
# first reproduce the error and print its FullName
try { Get-ChildItem C:\ThisFileDoesNotExist -ErrorAction Stop} catch { $Error[0].Exception.GetType().FullName }
System.Management.Automation.ItemNotFoundExceptio
# and now you can catch this specific error
try {
Get-ChildItem C:\ThisFileDoesNotExist -ErrorAction Stop
} catch [System.Management.Automation.ItemNotFoundException] {
Write-host "Item not found"
}
Tips
How to suppress progress bar for various commands
$global:progressPreference = 'silentlyContinue'
(tnc $server_ip).PingSucceeded
$global:progressPreference = 'Continue'
How to run a script bypassing the Execution Policy of the user
powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -WindowStyle Hidden -File <script_name>
How to start a PS1 script from scheduled tasks
If you are running the scipt with another account and not the logged in user or don't care about a window flashing for a while:

The optional arguments
are these:
-ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -WindowStyle Hidden -File __<script_name>__
If you don't want to have a powershell window flashing for a while

The optional arguments are these (change the path and the script filename):
vbscript:Execute("CreateObject(""Wscript.Shell"").Run ""powershell -NoLogo -Command """"& 'c:\some dir\script.ps1'"""""", 0 : window.close")
Creating a scheduled task from powershell
In this example we are creating a daily task that runs a powershell script
without any window popping up
$script = "C:\Users\user\bin\cleanup-screenshots.ps1"
$time = '5:15 AM'
$user = 'user'
$executable = "mshta"
$arguments = @'
vbscript:Execute("CreateObject(""Wscript.Shell"").Run ""powershell -NoProfile -ExecutionPolicy bypass -NoLogo -Command """"& '{script}'"""""", 0 : window.close")
'@
$arguments = $arguments -replace "{script}", $script
$actions = (New-ScheduledTaskAction -Execute $executable -Argument $arguments)
$trigger = New-ScheduledTaskTrigger -Daily -At $time
$principal = New-ScheduledTaskPrincipal -UserId $user
$settings = New-ScheduledTaskSettingsSet -RunOnlyIfNetworkAvailable
$task = New-ScheduledTask -Action $actions -Principal $principal -Trigger $trigger -Settings $settings
Register-ScheduledTask 'cleanup-screenshots' -InputObject $task
If you want the
powershell windows to be visible use this alternative
$script = "C:\Users\user\bin\cleanup-screenshots.ps1"
$time = '5:15 AM'
$user = 'user'
$executable = "C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe"
$arguments = "-NoProfile -ExecutionPolicy bypass -File `"$script`""
$actions = (New-ScheduledTaskAction -Execute $executable -Argument $arguments)
$trigger = New-ScheduledTaskTrigger -Daily -At $time
$principal = New-ScheduledTaskPrincipal -UserId $user
$settings = New-ScheduledTaskSettingsSet -RunOnlyIfNetworkAvailable
$task = New-ScheduledTask -Action $actions -Principal $principal -Trigger $trigger -Settings $settings
Register-ScheduledTask 'cleanup-screenshots' -InputObject $task
If you want to run something besides powershell scripts just change $exectuable and $arguments above
Finding files
A complex find operation
Everything after -Path is optional (it's also optional to use $FindDate= which helps to filter by last write time).
You can omit -File if you want to look both for files and directories (or replace it with -Directory)
The where-object expression can be as complex as necessary to narrow the files returned.
Get-ChildItem -File -Recurse -ErrorAction SilentlyContinue `
-Path C:\,D:\test `
-Include *.doc,*.docx `
-Exclude test* `
| Where-Object { $_.LastWriteTime -ge (Get-Date).AddDays(-8)}
# ALTERNATIVE WAYS TO DEFINE THE LastWriteTime:
# Get-Date -Year 2019 -Month 06 -Day 24
Find the most recent file in dir (recursively)
gci -recurse C:\some\dir | sort LastWriteTime | select -last 1
How to check if the session is elavated (started from Admin PowerShell)
function StopIfNotAnAdmin(){
# Am I running with admin rights?
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
$admin = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (! $admin) {
Write-Host "Please run me with administrator priviledges" -ForegroundColor Yellow
exit
}
}
How to execute powershell code in the context of the currently logged-in user from within a PS process that runs in the context of the SYSTEM user
# -------------------------------------
# An example that shows how to execute powershell code
# in the context of the currently logged-in user
# from within a process that runs in the context of the SYSTEM user
# (like code that is downloaded and executed by emar2_dl_task)
# -------------------------------------
function process_running_as_user($param1, $param2) {
$ScriptBlock={
param($param1, $param2)
write-verbose "running as user with params $param1, param2=$param2"
#vvvvvvvvvvvvvvvvvv
# YOUR CODE HERE!
#^^^^^^^^^^^^^^^^^^
}
# find the logged-in user
# The following command gives you the user but burried inside irelevant characters:
# Get-WmiObject Win32_LoggedOnUser | Select Antecedent -Unique | %{$_.Antecedent.ToString()}
$loggedin_user = (Get-WmiObject Win32_LoggedOnUser | Select Antecedent -Unique `
| %{"{0}\{1}" -f $_.Antecedent.ToString().Split('"')[1],$_.Antecedent.ToString().Split('"')[3]} `
| ?{ $_ -notlike '*\SYSTEM' -and $_ -notlike '*\LOCAL SERVICE' -and $_ -notlike '*\NETWORK SERVICE' `
-and $_ -notmatch '.*\\DWM-[0-9]' -and $_ -notmatch '.*\\UMFD-[0-9]'})
write-verbose "calling process_running_as_user with user $loggedin_user"
Invoke-CommandAs -ScriptBlock $ScriptBlock -AsInteractive $loggedin_user -args $param1, $param2
}
How to download and unzip an archive
New-TemporaryFile | %{rm $_; mkdir $_ >$null; cd $_} # create a temporary directory and cd into it (temp dir)
wget -Uri "http://foo.com/bar.zip" -OutFile t.zip
Expand-Archive t.zip
# create a text file for powershell commands if it doesn't exist
if (Test-Path $Profile) {} else {New-Item –Path $Profile –Type File –Force; echo "# startup commands">> $profile}
# edit your $profile
notepad $profile
Resources/Help
Troubleshooting
If installing modules fails try this command before:
[Net.ServicePointManager]::SecurityProtocol = "tls12"
Huge list of PowerShell commands
Experts from Learning PowerShell