2015-11-27 02:19:06 +03:00
---
category: tool
tool: powershell
contributors:
- ["Wouter Van Schandevijl", "https://github.com/laoujin"]
2020-08-03 12:22:18 +03:00
- ["Andrew Ryan Davis", "https://github.com/AndrewDavis1191"]
2015-11-27 02:19:06 +03:00
filename: LearnPowershell.ps1
---
2015-11-27 05:49:34 +03:00
PowerShell is the Windows scripting language and configuration management
framework from Microsoft built on the .NET Framework. Windows 7 and up ship
with PowerShell.
Nearly all examples below can be a part of a shell script or executed directly
in the shell.
2015-11-27 02:19:06 +03:00
2015-11-27 05:49:34 +03:00
A key difference with Bash is that it is mostly objects that you manipulate
2020-08-03 12:22:18 +03:00
rather than plain text. After years of evolving, it resembles Python a bit.
2015-11-27 02:19:06 +03:00
2020-01-12 11:44:21 +03:00
[Read more here. ](https://docs.microsoft.com/powershell/scripting/overview )
2015-11-27 02:19:06 +03:00
2020-08-20 00:19:02 +03:00
Powershell as a Language:
2016-01-05 22:17:33 +03:00
2020-08-16 06:05:08 +03:00
```powershell
2020-08-03 12:22:18 +03:00
2020-08-06 05:13:35 +03:00
# Single line comments start with a number symbol.
< #
Multi-line comments
like so
#>
2020-08-20 09:23:53 +03:00
2020-08-06 05:13:35 +03:00
####################################################
## 1. Primitive Datatypes and Operators
####################################################
# Numbers
3 # => 3
# Math
1 + 1 # => 2
8 - 1 # => 7
10 * 2 # => 20
35 / 5 # => 7.0
2020-08-20 22:12:01 +03:00
# 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.
2020-08-03 12:22:18 +03:00
[int]5 / [int]3 # => 1.66666666666667
[int]-5 / [int]3 # => -1.66666666666667
5.0 / 3.0 # => 1.66666666666667
-5.0 / 3.0 # => -1.66666666666667
2020-08-20 22:26:48 +03:00
[int]$result = 5 / 3
$result # => 2
2020-08-03 12:22:18 +03:00
# Modulo operation
7 % 3 # => 1
2020-08-20 22:12:01 +03:00
# Exponentiation requires longform or the built-in [Math] class.
2020-08-03 12:22:18 +03:00
[Math]::Pow(2,3) # => 8
2020-08-20 22:12:01 +03:00
# Enforce order of operations with parentheses.
2020-08-03 12:22:18 +03:00
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
2020-08-20 22:12:01 +03:00
# True and False are actually 1 and 0 but only support limited arithmetic.
# However, casting the bool to int resolves this.
2020-08-03 12:22:18 +03:00
$True + $True # => 2
$True * 8 # => '[System.Boolean] * [System.Int32]' is undefined
[int]$True * 8 # => 8
$False - 5 # => -5
2020-08-20 22:12:01 +03:00
# Comparison operators look at the numerical value of True and False.
2020-08-03 12:22:18 +03:00
0 -eq $False # => True
1 -eq $True # => True
2 -eq $True # => False
-5 -ne $False # => True
2020-08-20 22:12:01 +03:00
# Using boolean logical operators on ints casts to booleans for evaluation.
2020-08-03 12:22:18 +03:00
# but their non-cast value is returned
2020-08-17 00:46:42 +03:00
# Don't mix up with bool(ints) and bitwise -band/-bor
2020-08-03 12:22:18 +03:00
[bool ](0 ) # => False
[bool ](4 ) # => True
[bool ](-6 ) # => True
2020-08-17 00:46:42 +03:00
0 -band 2 # => 0
-5 -bor 0 # => -5
2020-08-03 12:22:18 +03:00
# 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
2020-08-20 22:12:01 +03:00
# (-is vs. -eq) -is checks if two objects are the same type.
2020-08-03 12:22:18 +03:00
# -eq checks if the objects have the same values.
2020-08-20 00:14:47 +03:00
# Note: we called '[Math]' from .NET previously without the preceeding
2020-08-20 22:12:01 +03:00
# namespaces. We can do the same with [Collections.ArrayList] if preferred.
2020-08-03 12:22:18 +03:00
[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
2020-08-20 20:46:03 +03:00
$b -is $a.GetType() # => True, a and b equal same type
2020-08-03 12:22:18 +03:00
$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
2020-08-20 22:12:01 +03:00
# You can also format using f-strings or formatted string literals.
2020-08-03 12:22:18 +03:00
$name = "Steve"
$age = 22
2020-08-20 22:12:01 +03:00
"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."
2020-08-03 12:22:18 +03:00
2020-08-20 10:11:48 +03:00
# 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
2020-08-20 20:46:03 +03:00
# Note that the function of '#' is removed, but '#' is still present
2020-08-20 10:11:48 +03:00
`#Get-Process # => Fail: not a recognized cmdlet
2020-08-03 12:22:18 +03:00
# $null is not an object
$null # => None
2020-08-17 07:14:26 +03:00
# $null, 0, and empty strings and arrays all evaluate to False.
2020-08-03 12:22:18 +03:00
# All other values are True
2020-08-20 22:12:01 +03:00
function Test-Value ($value) {
2020-08-17 07:14:26 +03:00
if ($value) {
Write-Output 'True'
}
else {
Write-Output 'False'
}
2020-08-03 12:22:18 +03:00
}
2020-08-17 07:14:26 +03:00
2020-08-20 22:12:01 +03:00
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
2020-08-03 12:22:18 +03:00
2020-08-17 07:14:26 +03:00
2020-08-03 12:22:18 +03:00
####################################################
## 2. Variables and Collections
####################################################
# Powershell uses the "Write-Output" function to print
2020-08-17 07:14:26 +03:00
Write-Output "I'm Posh. Nice to meet you!" # => I'm Posh. Nice to meet you!
2020-08-03 12:22:18 +03:00
# 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
2020-08-20 09:23:53 +03:00
2020-08-20 22:12:01 +03:00
# The default array object in Powershell is an fixed length array.
2020-08-03 12:22:18 +03:00
$defaultArray = "thing","thing2","thing3"
2020-08-20 22:12:01 +03:00
# you can add objects with '+=', but cannot remove objects.
2020-08-03 12:22:18 +03:00
$defaultArray.Add("thing4") # => Exception "Collection was of a fixed size."
2020-08-17 00:58:29 +03:00
# To have a more workable array, you'll want the .NET [ArrayList] class
# It is also worth noting that ArrayLists are significantly faster
2020-08-03 12:22:18 +03:00
# ArrayLists store sequences
[System.Collections.ArrayList]$array = @()
# You can start with a prefilled ArrayList
[System.Collections.ArrayList]$otherArray = @(4, 5, 6)
2015-11-27 04:37:30 +03:00
2020-08-20 22:12:01 +03:00
# 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
2020-08-03 12:22:18 +03:00
$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.
2015-11-27 05:49:34 +03:00
2020-08-03 12:22:18 +03:00
# Access a list like you would any array
$array[0] # => 1
# Look at the last element
$array[-1] # => 3
2015-11-27 05:49:34 +03:00
2020-08-03 12:22:18 +03:00
# Looking out of bounds returns nothing
$array[4] # blank line returned
2015-11-27 04:37:30 +03:00
2020-08-03 12:22:18 +03:00
# 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
2020-08-20 22:12:01 +03:00
# Examine length with "Count" (Note: "Length" on arrayList = each items length)
2020-08-03 12:22:18 +03:00
$array.Count # => 6
# Tuples are like arrays but are immutable.
2020-08-20 22:12:01 +03:00
# To use Tuples in powershell, you must use the .NET tuple class.
2020-08-03 12:22:18 +03:00
$tuple = [System.Tuple]::Create(1, 2, 3)
$tuple.Item(0) # => 1
$tuple.Item(0) = 3 # Raises a TypeError
2020-08-20 22:12:01 +03:00
# You can do some of the array methods on tuples, but they are limited.
$tuple.Length # => 3
2020-08-03 12:22:18 +03:00
$tuple + (4, 5, 6) # => Exception
2020-08-20 22:12:01 +03:00
$tuple[0..2] # => $null
2020-08-03 12:22:18 +03:00
2 -in $tuple # => False
2020-08-20 22:12:01 +03:00
# Hashtables store mappings from keys to values, similar to Dictionaries.
2020-08-03 12:22:18 +03:00
$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.
2020-08-20 22:12:01 +03:00
$filledHash.Keys # => ["one", "two", "three"]
2020-08-03 12:22:18 +03:00
# Get all values as an iterable with ".Values".
2020-08-20 22:12:01 +03:00
$filledHash.Values # => [1, 2, 3]
2020-08-03 12:22:18 +03:00
# Check for existence of keys or values in a hash with "-in"
"one" -in $filledHash.Keys # => True
2020-08-20 22:12:01 +03:00
1 -in $filledHash.Values # => False
2020-08-03 12:22:18 +03:00
# 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"
2020-08-20 22:12:01 +03:00
$filledHash["four"] = 4 # $filledHash["four"] is set to 4, running again does nothing
2020-08-03 12:22:18 +03:00
# 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."
}
2016-01-05 22:17:33 +03:00
2015-11-27 02:19:06 +03:00
2020-05-17 05:26:29 +03:00
< #
2020-08-03 12:22:18 +03:00
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
2020-05-17 05:26:29 +03:00
#>
2020-08-03 12:22:18 +03:00
$letters = ('a','b','c','d','e','f','g','h')
for($i=0; $i -le $letters.Count-1; $i++){
Write-Host $i, $letters[$i]
}
2020-05-17 05:26:29 +03:00
2020-08-03 12:22:18 +03:00
< #
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
2015-11-27 04:37:30 +03:00
}
2015-11-27 03:07:45 +03:00
2016-10-31 05:51:19 +03:00
# Switch statements are more powerful compared to most languages
2015-11-27 04:37:30 +03:00
$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" }
}
2020-08-03 12:22:18 +03:00
# Handle exceptions with a try/catch block
try {
# Use "throw" to raise an error
throw "This is an error"
2015-11-27 04:37:30 +03:00
}
2020-08-03 12:22:18 +03:00
catch {
Write-Output $Error.ExceptionMessage
}
finally {
Write-Output "We can clean up resources here"
2015-11-27 05:49:34 +03:00
}
2015-11-27 04:37:30 +03:00
2020-08-03 12:22:18 +03:00
# Writing to a file
$contents = @{"aa"= 12
"bb"= 21}
$contents | Export-CSV "$env:HOMEDRIVE\file.csv" # writes to a file
2015-11-27 04:37:30 +03:00
2020-08-03 12:22:18 +03:00
$contents = "test string here"
$contents | Out-File "$env:HOMEDRIVE\file.txt" # writes to another file
2015-11-27 04:37:30 +03:00
2020-08-03 12:22:18 +03:00
# Read file contents and convert to json
Get-Content "$env:HOMEDRIVE\file.csv" | ConvertTo-Json
2015-11-27 04:37:30 +03:00
2020-08-03 12:22:18 +03:00
####################################################
## 4. Functions
####################################################
2015-11-27 04:37:30 +03:00
2020-08-03 12:22:18 +03:00
# Use "function" to create new functions
# Keep the Verb-Noun naming convention for functions
function Add-Numbers {
$args[0] + $args[1]
}
2015-11-27 05:49:34 +03:00
2020-08-03 12:22:18 +03:00
Add-Numbers 1 2 # => 3
2015-11-27 05:49:34 +03:00
2020-08-03 12:22:18 +03:00
# Calling functions with parameters
function Add-ParamNumbers {
2020-08-20 22:12:01 +03:00
param( [int]$firstNumber, [int]$secondNumber )
$firstNumber + $secondNumber
2015-11-27 05:49:34 +03:00
}
2020-08-03 12:22:18 +03:00
Add-ParamNumbers -FirstNumber 1 -SecondNumber 2 # => 3
2015-11-27 05:49:34 +03:00
2016-10-31 05:51:19 +03:00
# Functions with named parameters, parameter attributes, parsable documentation
2015-11-27 05:49:34 +03:00
< #
.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() {
2020-08-03 12:22:18 +03:00
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline=$true, Mandatory=$true)]
[Alias('name')]
[string]$siteName,
[ValidateSet(3000,5000,8000)]
[int]$port = 3000
)
2020-08-20 22:12:01 +03:00
BEGIN { Write-Output 'Creating new website(s)' }
2020-08-06 05:13:35 +03:00
PROCESS { Write-Output "name: $siteName, port: $port" }
2020-08-20 22:12:01 +03:00
END { Write-Output 'Website(s) created' }
2015-11-27 05:49:34 +03:00
}
2015-11-27 03:07:45 +03:00
2020-08-03 12:22:18 +03:00
####################################################
## 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
####################################################
2020-08-20 22:12:01 +03:00
# Inheritance allows new child classes to be defined that inherit
# methods and variables from their parent class.
2020-08-03 12:22:18 +03:00
class Guitar : Instrument
{
[string]$Brand
[string]$SubType
[string]$ModelType
[string]$ModelNumber
}
$myGuitar = [Guitar]::new()
2020-08-20 22:12:01 +03:00
$myGuitar.Brand = "Taylor"
$myGuitar.SubType = "Acoustic"
$myGuitar.ModelType = "Presentation"
2020-08-03 12:22:18 +03:00
$myGuitar.ModelNumber = "PS14ce Blackwood"
$myGuitar.GetType()
< #
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False Guitar Instrument
#>
####################################################
## 7. Advanced
####################################################
2020-08-20 22:12:01 +03:00
# The powershell pipeline allows things like High-Order Functions.
2020-08-03 12:22:18 +03:00
2020-08-20 22:12:01 +03:00
# Group-Object is a handy cmdlet that does incredible things.
# It works much like a GROUP BY in SQL.
2020-08-03 12:22:18 +03:00
< #
2020-08-20 22:12:01 +03:00
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.
2020-08-03 12:22:18 +03:00
#>
2020-08-06 05:13:35 +03:00
Get-Process | Foreach-Object ProcessName | Group-Object
2020-08-03 12:22:18 +03:00
2020-08-20 22:12:01 +03:00
# Useful pipeline examples are iteration and filtering.
2020-08-20 00:14:47 +03:00
1..10 | ForEach-Object { "Loop number $PSITEM" }
2020-08-20 09:23:53 +03:00
1..10 | Where-Object { $PSITEM -gt 5 } | ConvertTo-Json
2020-08-20 00:14:47 +03:00
2020-08-20 22:12:01 +03:00
# 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.
2020-08-20 00:14:47 +03:00
2020-08-03 12:22:18 +03:00
< #
2020-08-20 22:12:01 +03:00
Asynchronous functions exist in the form of jobs.
Typically a procedural language,
Powershell can operate non-blocking functions when invoked as Jobs.
2020-08-03 12:22:18 +03:00
#>
2020-08-20 22:12:01 +03:00
# This function is known to be non-optimized, and therefore slow.
2020-08-03 12:22:18 +03:00
$installedApps = Get-CimInstance -ClassName Win32_Product
2020-08-20 22:12:01 +03:00
# If we had a script, it would hang at this func for a period of time.
2020-08-03 12:22:18 +03:00
$scriptBlock = {Get-CimInstance -ClassName Win32_Product}
Start-Job -ScriptBlock $scriptBlock
2020-08-20 22:12:01 +03:00
# This will start a background job that runs the command.
# You can then obtain the status of jobs and their returned results.
2020-08-03 12:22:18 +03:00
$allJobs = Get-Job
2020-08-20 22:12:01 +03:00
$jobResponse = Get-Job | Receive-Job
2020-08-03 12:22:18 +03:00
2020-08-20 22:12:01 +03:00
# Math is built in to powershell and has many functions.
2020-08-03 12:22:18 +03:00
$r=2
$pi=[math]::pi
$r2=[math]::pow( $r, 2 )
2020-08-20 22:12:01 +03:00
$area = $pi*$r2
$area
2020-08-03 12:22:18 +03:00
2020-08-20 22:12:01 +03:00
# To see all possibilities, check the members.
2020-08-03 12:22:18 +03:00
[System.Math] | Get-Member -Static -MemberType All
2020-08-20 22:12:01 +03:00
2020-08-03 12:22:18 +03:00
< #
2020-08-20 22:12:01 +03:00
This is a silly one:
2020-08-03 12:22:18 +03:00
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
2020-08-20 22:12:01 +03:00
based on an arbitrary array without mutating the original array.
Let's see one way to do that and introduce another data structure.
2020-08-03 12:22:18 +03:00
#>
2020-08-20 22:12:01 +03:00
$targetArray = 'a','b','c','d','e','f','g','h','i','j','k','l','m'
2020-08-03 12:22:18 +03:00
2020-10-07 04:02:35 +03:00
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++) {
2020-08-06 05:13:35 +03:00
if ($index -lt $start) {
2020-10-07 04:02:35 +03:00
$firstSectionArray.Add($array[$index]) > $null
2020-08-03 12:22:18 +03:00
}
2020-08-06 05:13:35 +03:00
elseif ($index -ge $start -and $index -le $end) {
2020-10-07 04:02:35 +03:00
$stack.Push($array[$index])
2020-08-03 12:22:18 +03:00
}
2020-08-17 07:14:26 +03:00
else {
2020-10-07 04:02:35 +03:00
$secondSectionArray.Add($array[$index]) > $null
2020-08-03 12:22:18 +03:00
}
}
2020-08-20 22:12:01 +03:00
$finalArray = $firstSectionArray + $stack.ToArray() + $secondSectionArray
2020-10-07 04:02:35 +03:00
return $finalArray
2020-08-06 05:13:35 +03:00
}
2020-10-07 04:02:35 +03:00
Format-Range 2 6 $targetArray
# => 'a','b','g','f','e','d','c','h','i','j','k','l','m'
2020-08-17 07:14:26 +03:00
2020-08-20 22:12:01 +03:00
# 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.
2020-08-06 05:13:35 +03:00
function Format-Range ($start, $end) {
while ($start -lt $end)
{
$temp = $targetArray[$start]
$targetArray[$start] = $targetArray[$end]
$targetArray[$end] = $temp
$start++
$end--
}
return $targetArray
2020-05-17 05:26:29 +03:00
}
2020-08-17 07:14:26 +03:00
2020-08-20 22:12:01 +03:00
Format-Range 2 6 # => 'a','b','g','f','e','d','c','h','i','j','k','l','m'
2015-11-27 03:07:45 +03:00
```
2020-08-20 00:19:02 +03:00
Powershell as a Tool:
2015-11-27 03:07:45 +03:00
2020-08-03 12:22:18 +03:00
Getting Help:
2020-08-17 07:14:26 +03:00
2020-08-03 12:22:18 +03:00
```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
2015-11-27 03:07:45 +03:00
2020-08-20 00:14:47 +03:00
Show-Command Get-WinEvent # Display GUI to fill in the parameters
2016-01-05 22:17:33 +03:00
2020-08-03 12:22:18 +03:00
Update-Help # Run as admin
```
If you are uncertain about your environment:
2020-08-20 22:40:18 +03:00
2020-08-03 12:22:18 +03:00
```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
2020-08-20 22:12:01 +03:00
# Calling external commands, executables,
# and functions with the call operator.
# Exe paths with arguments passed or containing spaces can create issues.
2020-08-20 10:10:08 +03:00
C:\Program Files\dotnet\dotnet.exe
2020-08-20 22:12:01 +03:00
# 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
2020-08-20 10:10:08 +03:00
"C:\Program Files\dotnet\dotnet.exe"
2020-08-20 22:12:01 +03:00
C:\Program Files\dotnet\dotnet.exe # returns string rather than execute
2020-08-20 10:10:08 +03:00
& "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
2020-08-20 22:12:01 +03:00
# 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.
2020-08-20 00:14:47 +03:00
# Notice the variables are scoped
$i = 2
2020-08-20 22:26:48 +03:00
$scriptBlock = { $i=5; Write-Output $i }
& $scriptBlock # => 5
2020-08-20 00:14:47 +03:00
$i # => 2
invoke-expression ' $i=5; Write-Output $i ' # => 5
$i # => 5
# Alternatively, to preserve changes to public variables
2020-08-20 22:12:01 +03:00
# you can use "Dot-Sourcing". This will run in the current scope.
2020-08-20 00:14:47 +03:00
$x=1
&{$x=2}; $x # => 1
2020-08-20 10:10:08 +03:00
.{$x=2};$x # => 2
2020-08-20 22:12:01 +03:00
# Remoting into computers is easy.
2020-08-03 12:22:18 +03:00
Enter-PSSession -ComputerName RemoteComputer
2020-08-20 00:14:47 +03:00
2020-08-20 22:12:01 +03:00
# Once remoted in, you can run commands as if you're local.
2020-08-03 12:22:18 +03:00
RemoteComputer\PS> Get-Process powershell
2020-08-20 00:14:47 +03:00
2020-08-03 12:22:18 +03:00
< #
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
< #
2020-08-20 22:12:01 +03:00
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.
2020-08-03 12:22:18 +03:00
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'
)
2020-08-20 22:12:01 +03:00
[scriptblock]$script = {
2020-08-03 12:22:18 +03:00
Get-Service -DisplayName 'Task Scheduler'
2015-11-27 03:07:45 +03:00
}
2020-08-03 12:22:18 +03:00
foreach ($server in $serverList) {
2020-08-20 22:12:01 +03:00
$cmdSplat = @{
ComputerName = $server
2020-08-03 12:22:18 +03:00
JobName = 'checkService'
2020-08-20 22:12:01 +03:00
ScriptBlock = $script
2020-08-03 12:22:18 +03:00
AsJob = $true
ErrorAction = 'SilentlyContinue'
}
2020-08-20 22:12:01 +03:00
Invoke-Command @cmdSplat | Out-Null
2020-08-03 12:22:18 +03:00
}
< #
2020-08-20 22:12:01 +03:00
Here we've invoked jobs across many servers.
We can now Receive-Job and see if they're all running.
2020-08-03 12:22:18 +03:00
Now scale this up 100x as many servers :)
#>
2015-11-27 05:49:34 +03:00
```
Interesting Projects
2016-01-08 21:50:57 +03:00
2015-11-27 06:05:53 +03:00
* [Channel9 ](https://channel9.msdn.com/Search?term=powershell%20pipeline#ch9Search&lang-en=en ) PowerShell tutorials
2020-08-20 00:14:47 +03:00
* [KevinMarquette's Powershell Blog ](https://powershellexplained.com/ ) Excellent blog that goes into great detail on Powershell
2015-11-27 06:05:53 +03:00
* [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!)
2020-05-17 10:18:21 +03:00
* [Oh-My-Posh ](https://github.com/JanDeDobbeleer/oh-my-posh ) Shell customization similar to the popular Oh-My-Zsh on Mac
2015-11-27 05:49:34 +03:00
* [PSake ](https://github.com/psake/psake ) Build automation tool
* [Pester ](https://github.com/pester/Pester ) BDD Testing Framework
2015-11-27 06:05:53 +03:00
* [Jump-Location ](https://github.com/tkellogg/Jump-Location ) Powershell `cd` that reads your mind
2020-01-12 11:43:09 +03:00
* [PowerShell Community Extensions ](https://github.com/Pscx/Pscx )
2020-08-20 00:14:47 +03:00
* [More on the Powershell Pipeline Issue ](https://github.com/PowerShell/PowerShell/issues/1908 )