mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2024-12-23 17:41:41 +00:00
Revamping Powershell
Changing to reflect current capabilities and draw similarities to other languages in terms of capabilities
This commit is contained in:
parent
177e5013c1
commit
b30a88cd31
@ -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,116 +14,347 @@ 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://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
|
||||
```
|
||||
|
||||
Getting help:
|
||||
|
||||
```powershell
|
||||
# Find commands
|
||||
Get-Command about_* # alias: gcm
|
||||
Get-Command -Verb Add
|
||||
Get-Alias ps
|
||||
Get-Alias -Definition Get-Process
|
||||
|
||||
Get-Help ps | less # alias: help
|
||||
ps | Get-Member # alias: gm
|
||||
|
||||
Show-Command Get-EventLog # Display GUI to fill in the parameters
|
||||
|
||||
Update-Help # Run as admin
|
||||
```
|
||||
|
||||
The tutorial starts here:
|
||||
|
||||
```powershell
|
||||
# As you already figured, comments start with #
|
||||
# Single line comments start with a number symbol.
|
||||
|
||||
<#
|
||||
Multi-line comments
|
||||
like so
|
||||
#>
|
||||
|
||||
# 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
|
||||
####################################################
|
||||
## 1. Primitive Datatypes and Operators
|
||||
####################################################
|
||||
|
||||
# Each command starts on a new line, or after a semicolon:
|
||||
echo 'This is the first line'; echo 'This is the second line'
|
||||
# Numbers
|
||||
3 # => 3
|
||||
|
||||
# Declaring a variable looks like this:
|
||||
$aString="Some string"
|
||||
# Or like this:
|
||||
$aNumber = 5 -as [double]
|
||||
$aList = 1,2,3,4,5
|
||||
# Reverse an array *Note this is a mutation on the existing array
|
||||
[array]::Reverse($aList)
|
||||
$anEmptyList = @()
|
||||
$aString = $aList -join '--' # yes, -split exists also
|
||||
$aHashtable = @{name1='val1'; name2='val2'}
|
||||
# Math
|
||||
1 + 1 # => 2
|
||||
8 - 1 # => 7
|
||||
10 * 2 # => 20
|
||||
35 / 5 # => 7.0
|
||||
|
||||
# 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
|
||||
# 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 # => 2
|
||||
|
||||
# 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`
|
||||
# Modulo operation
|
||||
7 % 3 # => 1
|
||||
|
||||
# Find the datatype of variables or properties you're working with
|
||||
$true.GetType()
|
||||
$aHashtable.name2.GetType()
|
||||
# Exponentiation requires longform or the built-in [Math] class
|
||||
[Math]::Pow(2,3) # => 8
|
||||
|
||||
# Inline another file (dot operator)
|
||||
. .\otherScriptName.ps1
|
||||
# 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 them to booleans for evaluation
|
||||
# but their non-cast value is returned
|
||||
# Don't mix up with bool(ints) and bitwise and/or (&,|)
|
||||
[bool](0) # => False
|
||||
[bool](4) # => True
|
||||
[bool](-6) # => True
|
||||
0 -and 2 # => 0
|
||||
-5 -or 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.
|
||||
[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."
|
||||
|
||||
# $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) {
|
||||
if ($value) {Write-Output 'True'}
|
||||
else {Write-Output 'False'}
|
||||
}
|
||||
test ($null) # => False
|
||||
test (0) # => False
|
||||
test ("") # => False
|
||||
test [] # => True
|
||||
test ({}) # => True
|
||||
test @() # => False
|
||||
|
||||
####################################################
|
||||
## 2. Variables and Collections
|
||||
####################################################
|
||||
|
||||
# Powershell uses the "Write-Output" function to print
|
||||
Write-Output "I'm Powershell. Nice to meet you!" # => I'm Powershell. 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 immutable array
|
||||
$defaultArray = "thing","thing2","thing3"
|
||||
# you are unable to add or remove objects
|
||||
$defaultArray.Add("thing4") # => Exception "Collection was of a fixed size."
|
||||
# To have a mutable array, you will need to use the .NET ArrayList class
|
||||
|
||||
# ArrayLists store sequences
|
||||
[System.Collections.ArrayList]$array = @()
|
||||
# You can start with a prefilled ArrayList
|
||||
[System.Collections.ArrayList]$otherArray = @(4, 5, 6)
|
||||
|
||||
# Add stuff to the end of a list with add (Note: it produces output, so 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 the end with index of count of objects-1 as arrays are indexed starting 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 the length with "Count" (Note: Length method on arrayList = each items length)
|
||||
$array.Count # => 6
|
||||
|
||||
|
||||
### 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'
|
||||
# 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, run again and it 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
|
||||
@ -137,85 +369,52 @@ switch($val) {
|
||||
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
|
||||
# Writing to a file
|
||||
$contents = @{"aa"= 12
|
||||
"bb"= 21}
|
||||
$contents | Export-CSV "$env:HOMEDRIVE\file.csv" # writes to a file
|
||||
|
||||
# Get all providers in your session
|
||||
Get-PSProvider
|
||||
$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
|
||||
|
||||
|
||||
### 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
|
||||
####################################################
|
||||
## 4. Functions
|
||||
####################################################
|
||||
|
||||
# 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
|
||||
|
||||
# ` 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
|
||||
|
||||
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" }
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
### Functions
|
||||
# The [string] attribute is optional.
|
||||
# Function names should follow Verb-Noun convention
|
||||
function Get-Foo([string]$name) {
|
||||
echo "Hey $name, have a function"
|
||||
# Use "function" to create new functions
|
||||
# Keep the Verb-Noun naming convention for functions
|
||||
function Add-Numbers {
|
||||
$args[0] + $args[1]
|
||||
}
|
||||
|
||||
# Calling your function
|
||||
Get-Foo "Say my name"
|
||||
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
|
||||
<#
|
||||
@ -246,97 +445,254 @@ function New-Website() {
|
||||
}
|
||||
|
||||
|
||||
### 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'
|
||||
####################################################
|
||||
## 5. Modules
|
||||
####################################################
|
||||
|
||||
# Unsure how that .NET method is called again?
|
||||
'string' | gm
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# You can also pass arguments to a Function with a hash table
|
||||
# This is called Splatting
|
||||
# Normal Command
|
||||
Export-Csv -InputObject $csv -Path 'c:\mypath' -Encoding UTF8 -NoTypeInformation
|
||||
# With Splatting
|
||||
$csvArguments = @{
|
||||
InputObject = $csv
|
||||
Path = 'c:\mypath'
|
||||
Encoding = 'UTF8'
|
||||
NoTypeInformation = $true
|
||||
$query = "SELECT * FROM dbo.sometable"
|
||||
$queryParams = @{
|
||||
SqlInstance = 'testInstance'
|
||||
Database = 'testDatabase'
|
||||
Query = $query
|
||||
}
|
||||
Export-Csv @csvArguments
|
||||
Invoke-DbaQuery @queryParams
|
||||
|
||||
# Open Windows Explorer in working directory
|
||||
Invoke-Item .
|
||||
# Or the alias
|
||||
ii .
|
||||
# You can get specific functions from a module
|
||||
Import-Module -Function Invoke-DbaQuery
|
||||
|
||||
# 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()
|
||||
# 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 us to do things like High-Order Functions
|
||||
|
||||
# Group Object is a handy command that does incredible things for us
|
||||
# It works much like a GROUP BY in SQL would
|
||||
|
||||
<#
|
||||
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 ProcessName | Group-Object
|
||||
|
||||
<#
|
||||
Asynchronous functions exist in the form of jobs
|
||||
Typically a procedural language
|
||||
Powershell can operate many non-blocking functions when invoked as Jobs
|
||||
#>
|
||||
|
||||
# This function is commonly 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
|
||||
Let's see one way to do that
|
||||
#>
|
||||
|
||||
$testArray = 'a','b','c','d','e','f','g','h','i','j','k','l','m','n'
|
||||
|
||||
function Reverse-Range ($start, $end) {
|
||||
[System.Collections.ArrayList]$newArray = @()
|
||||
[System.Collections.ArrayList]$secondArray = @()
|
||||
[System.Collections.Stack]$stack = @()
|
||||
for ($i = 0; $i -lt $testArray.Length; $i++) {
|
||||
if ($i -lt $start) {
|
||||
$newArray.Add($testArray[$i]) > $null
|
||||
}
|
||||
elseif ($i -ge $start -and $i -le $end) {
|
||||
$stack.push($testArray[$i])
|
||||
}
|
||||
elseif ($i -gt $end) {
|
||||
$secondArray.Add($testArray[$i]) > $null
|
||||
}
|
||||
}
|
||||
$endArray = $newArray + $stack.ToArray() + $secondArray
|
||||
Write-Output $endArray
|
||||
}
|
||||
```
|
||||
Powershell as a Tool:
|
||||
|
||||
Getting Help:
|
||||
```Powershell
|
||||
# Find commands
|
||||
Get-Command about_* # alias: gcm
|
||||
Get-Command -Verb Add
|
||||
Get-Alias ps
|
||||
Get-Alias -Definition Get-Process
|
||||
|
||||
Get-Help ps | less # alias: help
|
||||
ps | Get-Member # alias: gm
|
||||
|
||||
Show-Command Get-EventLog # 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
|
||||
|
||||
Configuring your shell
|
||||
# Current PowerShell version:
|
||||
$PSVersionTable
|
||||
```
|
||||
|
||||
```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
|
||||
```Powershell
|
||||
# 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'
|
||||
}
|
||||
# More info: `help about_profiles`
|
||||
# For a more useful shell, be sure to check the project PSReadLine below
|
||||
|
||||
foreach ($server in $serverList) {
|
||||
$CmdSplat = @{
|
||||
ComputerName = $Server
|
||||
JobName = 'checkService'
|
||||
ScriptBlock = $Script
|
||||
AsJob = $true
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
Invoke-Command @CmdSplat | Out-Null
|
||||
}
|
||||
|
||||
<#
|
||||
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
|
||||
@ -350,10 +706,3 @@ Interesting Projects
|
||||
* [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](https://github.com/Pscx/Pscx)
|
||||
|
||||
Not covered
|
||||
|
||||
* WMI: Windows Management Intrumentation (Get-CimInstance)
|
||||
* Multitasking: Start-Job -scriptBlock {...},
|
||||
* Code Signing
|
||||
* Remoting (Enter-PSSession/Exit-PSSession; Invoke-Command)
|
||||
|
Loading…
Reference in New Issue
Block a user