2015-11-27 02:19:06 +03:00
---
category: tool
tool: powershell
contributors:
- ["Wouter Van Schandevijl", "https://github.com/laoujin"]
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
rather than plain text.
2015-11-27 02:19:06 +03:00
[Read more here. ](https://technet.microsoft.com/en-us/library/bb978526.aspx )
2015-11-27 03:07:45 +03:00
If you are uncertain about your environment:
2016-01-05 22:17:33 +03:00
2016-10-23 15:22:45 +03:00
```powershell
2015-11-27 03:07:45 +03:00
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
```
2015-11-27 04:37:30 +03:00
Getting help:
2016-01-05 22:17:33 +03:00
2016-11-22 14:45:45 +03:00
```powershell
2015-11-27 04:37:30 +03:00
# 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 05:49:34 +03:00
Show-Command Get-EventLog # Display GUI to fill in the parameters
Update-Help # Run as admin
2015-11-27 04:37:30 +03:00
```
2015-11-27 03:07:45 +03:00
The tutorial starts here:
2016-01-05 22:17:33 +03:00
2016-11-22 14:45:45 +03:00
```powershell
2015-11-27 02:19:06 +03:00
# As you already figured, comments start with #
# Simple hello world example:
echo Hello world!
# echo is an alias for Write-Output (=cmdlet)
# Most cmdlets and functions follow the Verb-Noun naming convention
2015-11-27 05:49:34 +03:00
# Each command starts on a new line, or after a semicolon:
2015-11-27 02:19:06 +03:00
echo 'This is the first line'; echo 'This is the second line'
# Declaring a variable looks like this:
2015-11-27 03:07:45 +03:00
$aString="Some string"
2015-11-27 02:19:06 +03:00
# Or like this:
2015-11-27 05:49:34 +03:00
$aNumber = 5 -as [double]
2015-11-27 04:37:30 +03:00
$aList = 1,2,3,4,5
2018-11-08 05:48:33 +03:00
$anEmptyList = @()
2015-11-27 05:49:34 +03:00
$aString = $aList -join '--' # yes, -split exists also
2015-11-27 04:37:30 +03:00
$aHashtable = @{name1='val1'; name2='val2'}
2015-11-27 02:19:06 +03:00
2015-11-27 04:37:30 +03:00
# Using variables:
2015-11-27 03:07:45 +03:00
echo $aString
echo "Interpolation: $aString"
2016-11-23 20:43:37 +03:00
echo "$aString has length of $($aString.Length)"
2015-11-27 03:07:45 +03:00
echo '$aString'
2015-11-27 02:19:06 +03:00
echo @"
This is a Here-String
2015-11-27 03:07:45 +03:00
$aString
2015-11-27 02:19:06 +03:00
"@
# Note that ' (single quote) won't expand the variables!
# Here-Strings also work with single quote
# Builtin variables:
# There are some useful builtin variables, like
echo "Booleans: $TRUE and $FALSE"
echo "Empty value: $NULL"
echo "Last program's return value: $?"
2015-11-27 03:07:45 +03:00
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: $^"
2015-11-27 02:19:06 +03:00
echo "Script's PID: $PID"
2015-11-27 03:07:45 +03:00
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"
2015-11-27 05:49:34 +03:00
echo "Unbound arguments: $($Args -join ', ')."
2015-11-27 03:07:45 +03:00
# More builtins: `help about_Automatic_Variables`
2015-11-27 02:19:06 +03:00
2015-11-27 05:49:34 +03:00
# Inline another file (dot operator)
. .\otherScriptName.ps1
2015-11-27 03:07:45 +03:00
2015-11-27 05:49:34 +03:00
### Control Flow
2015-11-27 04:37:30 +03:00
# 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'
}
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 }
{ 'x' -contains 'x'} { "FALSE! -contains is for lists!"; break }
default { "Others" }
}
# The classic for
for($i = 1; $i -le 10; $i++) {
"Loop number $i"
}
# Or shorter
1..10 | % { "Loop number $_" }
# PowerShell also offers
foreach ($var in 'val1','val2','val3') { echo $var }
# while () {}
# do {} while ()
# do {} until ()
2015-11-27 05:49:34 +03:00
# Exception handling
try {} catch {} finally {}
try {} catch [System.NullReferenceException] {
echo $_.Exception | Format-List -Force
}
2015-11-27 04:37:30 +03:00
2015-11-27 05:49:34 +03:00
### Providers
2015-11-27 04:37:30 +03:00
# List files and directories in the current directory
ls # or `dir`
cd ~ # goto home
Get-Alias ls # -> Get-ChildItem
# Uh!? These cmdlets have generic names because unlike other scripting
# languages, PowerShell does not only operate in the current directory.
cd HKCU: # go to the HKEY_CURRENT_USER registry hive
# Get all providers in your session
Get-PSProvider
2015-11-27 05:49:34 +03:00
### Pipeline
2015-11-27 04:37:30 +03:00
# 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
# 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
2015-11-27 05:49:34 +03:00
# 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 foo([string]$name) {
echo "Hey $name, have a function"
}
# Calling your function
foo "Say my name"
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() {
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline=$true, Mandatory=$true)]
[Alias('name')]
[string]$siteName,
[ValidateSet(3000,5000,8000)]
[int]$port = 3000
)
BEGIN { Write-Verbose 'Creating new website(s)' }
PROCESS { echo "name: $siteName, port: $port" }
END { Write-Verbose 'Website(s) created' }
}
2015-11-27 03:07:45 +03:00
2015-11-27 05:49:34 +03:00
### 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'
2015-11-27 03:07:45 +03:00
2015-11-27 05:49:34 +03:00
# Unsure how that .NET method is called again?
'string' | gm
# Syntax for calling static .NET methods
2019-02-03 04:41:28 +03:00
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')
2015-11-27 05:49:34 +03:00
# Note that .NET functions MUST be called with parentheses
2016-01-08 21:50:57 +03:00
# 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
2015-11-27 05:49:34 +03:00
$writer = New-Object System.IO.StreamWriter($path, $true)
$writer.Write([Environment]::NewLine)
2016-01-08 21:50:57 +03:00
$writer.Dispose()
2015-11-27 05:49:34 +03:00
### 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")
2016-01-08 21:50:57 +03:00
2015-11-27 05:49:34 +03:00
# 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
2016-01-08 21:50:57 +03:00
# Aliases are: pushd and popd
2015-11-27 05:49:34 +03:00
# Unblock a directory after download
Get-ChildItem -Recurse | Unblock-File
# Open Windows Explorer in working directory
ii .
2015-11-27 06:05:53 +03:00
# 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()
2015-11-27 03:07:45 +03:00
```
Configuring your shell
2016-01-05 22:17:33 +03:00
2016-11-22 14:45:45 +03:00
```powershell
2015-11-27 03:07:45 +03:00
# $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
}
# More info: `help about_profiles`
2016-10-31 05:51:19 +03:00
# For a more useful shell, be sure to check the project PSReadLine below
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
* [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!)
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
* [PowerShell Community Extensions ](http://pscx.codeplex.com/ ) (Dead)
2015-11-27 05:49:34 +03:00
Not covered
2016-01-08 21:50:57 +03:00
2015-11-27 05:49:34 +03:00
* WMI: Windows Management Intrumentation (Get-CimInstance)
* Multitasking: Start-Job -scriptBlock {...},
* Code Signing
2015-11-27 07:44:22 +03:00
* Remoting (Enter-PSSession/Exit-PSSession; Invoke-Command)