Skip to content

Latest commit

 

History

History
2959 lines (2465 loc) · 103 KB

README.md

File metadata and controls

2959 lines (2465 loc) · 103 KB
Copyright(c) 2019-
Author: Chaitanya Tejaswi (github.com/CRTejaswi)    License: GPL v3.0+

PowerShell

Personal notes.

Resources

Link Description
IronScripter Monthly PS challenges.
powershell.org PS news.
MS DevBlog Short pieces on PS use-cases.
Awesome PS Collection of PS projects/utilities.
GitHub Source-Code for PS Core.
Jeff Hicks The No-nonsense programmer; The 'David Beazley' of PS.
Adam Bertram
Doug Finke Guy who wrote ImportExcel - a module to access xlsx files without installing MSOffice.
TechSnips [1] [2] Short, infomative clips on various use-cases of PS.
Warren Frame [PS recipies] Guy who wrote PSSQLite - a module to access SQLite databases in PS.
Prateek Singh Indian guy who uses PS for fun.

Articles: OLD, NEW, NEW

Index

General

  • Update (from PS)

    iex "& { $(irm https://aka.ms/install-powershell.ps1) } -UseMSI"
    
  • Clear Command History
    Press Ctrl + F7

  • Clear RecycleBin
    Clear-RecycleBin

  • Disable Quick Access, Recent Items & File-Explorer History.
    Quick Access (Right-Click) >> Options >> Open File Explorer to: "This PC" >> Uncheck Privacy Options.

  • Uninstall built-in apps

    Get-AppxPackage *3dbuilder* | Remove-AppxPackage
    Get-AppxPackage *getstarted* | Remove-AppxPackage
    Get-AppxPackage *windowsalarms* | Remove-AppxPackage
    Get-AppxPackage *windowscalculator* | Remove-AppxPackage
    Get-AppxPackage *windowscommunicationapps* | Remove-AppxPackage
    Get-AppxPackage *windowsmaps* | Remove-AppxPackage
    Get-AppxPackage *windowsphone* | Remove-AppxPackage
    Get-AppxPackage *windowsstore* | Remove-AppxPackage
    Get-AppxPackage *zunemusic* | Remove-AppxPackage
    Get-AppxPackage *zunevideo* | Remove-AppxPackage
    Get-AppxPackage *bingfinance* | Remove-AppxPackage
    Get-AppxPackage *bingnews* | Remove-AppxPackage
    Get-AppxPackage *bingweather* | Remove-AppxPackage
    Get-AppxPackage *bingsports* | Remove-AppxPackage
    Get-AppxPackage *photos* | Remove-AppxPackage
    Get-AppxPackage *soundrecorder* | Remove-AppxPackage
    Get-AppxPackage *xboxapp* | Remove-AppxPackage
    
  • Create/Modify Environment Variables
    You can create/modify User/System environment variables using this one-liner in Powershell.

    [Environment]::SetEnvironmentVariable("Youtube_ApiKey", "<API_KEY>", "User")
    [Environment]::SetEnvironmentVariable("Youtube_ApiKey", "<API_KEY>", "Machine")
    
  • Create New File/Folder

    ni -path 'C:\Users\CRTejaswi\Desktop' -ItemType 'directory' -name 'myTEST'
    ni -path 'C:\Users\CRTejaswi\Desktop\myTEST' -ItemType 'file' -name 'myTEST.md'
    
  • Get storage directory of binary file.

    gps firefox | ls
    
  • Common Aliases

    man -> Get-Help    (to lookup command syntax)
    gcm -> Get-Command (to lookup command syntax)
    gm  -> Get-Member  (to learn more about an object)
    gal -> Get-Alias
    
    gc  -> Get-Content (or 'cat'; Read from file)
    sc  -> Set-Content (Write to file)
    gcb -> Get-Clipboard (Copy from clipboard)
    scb -> Set-Clipboard (Copy to clipboard)
    gps -> Get-Process
    
    ft  -> Format-Table
    fl  -> Format-List
    
    gsv -> Get-Service
    gin -> Get-ComputerInfo
    gtz -> Get-TimeZone
    
    gwmi -> Get-WmiObject
    gjb  -> Get-Job
    
    cls -> Clear-Host ('clear shell')
    clc -> Clear-Content
    clv -> Clear-Variable
    cli -> Clear-Item
    clp -> Clear-ItemProperty
    
    pushd, popd -> Push-Location, Pop-Location
    copy, move  -> Copy-Item, Move-Item
    history     -> Get-History
    
  • Progress-Bar
    eg. Display a countdown timer using a progressbar.

    $seconds = 60
    1..$seconds | forEach {
        $percent = ($_ * 100)/$seconds;
        Write-Progress -Activity 'Waiting for You to Finish' `
                       -Status "$($seconds - $_) seconds remaining ..." `
                       -PercentComplete $percent;
        Start-Sleep -Seconds 1
    }
  • Remove BOM from UTF-8 Files [BROKEN]

    function Remove-BOM ($OldPath, $NewPath){
        $content  = Get-Content $OldPath -Raw
        $encoding = New-Object System.Text.UTF8Encoding $False
        [System.IO.File]::WriteAllLines($NewPath, $content, $encoding)
    }
    Remove-BOM -Path .\test.json
  • Work with file/folder
    Save filepath in $PROFILE variable.
    Open file/folder using editor/cd.

    $ImagingSatellite="B:\CRTejaswi\Codes\resources\topics\imaging_satellite\imaging_satellite.md"
    nano $ImagingSatellite
    cd (ls $ImagingSatellite).Directory
    
  • Check filesize before downloading

    (iwr $url -Method Head).Headers.'Content-Length'/1MB
    
  • Execute lines from a text file

    & { Invoke-Expression (Get-Content -Raw README.md) }
    & { iex (cat -Raw README.md) }
  • List all open GUI windows

    gps | where {$_.MainWindowTitle -ne ""} | select MainWindowTitle
  • Binding Keyboard Shortcuts [BROKEN]
    Use PSReadline module cmdlets.
    eg. Press Ctrl+H to display a GridView of command history. Select a cmd to execute.

    Set-PSReadlineKeyHandler -Chord Ctrl+H -ScriptBlock {
        Get-History | Out-GridView -Title 'Command History' -PassThru | Invoke-History
    }
  • Timing script executions
    The naive way is to use Get-Date.

    $start = Get-Date
    ...
    $stop  = Get-Date
    $duration = $stop - $start; $duration.TotalMilliseconds;

    You can also use StopWatch (.NET class).

    $stopwatch = [Diagnostics.StopWatch]::StartNew()
    ...
    $stopwatch.Stop()
    $stopwatch.ElapsedMilliseconds
    ...
    $stopwatch.Restart()
    ...
    $stopwatch.Stop()
    $stopwatch.ElapsedMilliseconds

Bulk

  • Move files to current directory

    move ((ls B:\CRTejaswi\Downloads\) -match '^\d{1}.mp4').FullName .

    Copy/Move files using regex.

    ls $Path | where {$_.Name -match 'ca'} | forEach {copy $_.fullname .}
  • Emails

    Send-MailMessage `
        -from "aeronfinium@gmail.com" -To "abc@gmail.com","xyz@gmail.com" `
        -SmtpServer "smtp.gmail.com" -usessl -Credential (Get-Credential) `
        -Subject "Test Message" `
        -Body "Sent from PS shell -- Chaitanya Tejaswi" `
        -Attachments test.pdf,test.mp3,test.mp4

    Get all email addresses from HTML object

    $response.Links | forEach {
        if ($_.href.ToLower().StartsWith("mailto:")){ $_.href.SubString(7) }
    }
  • Merge files
    Text files.

    Get-Content test.txt,test.md,test.html,test.py | Set-Content OUTPUT.md
    cat test.txt,test.md,test.html,test.py | sc OUTPUT.md

    Image files - vertical/horizontal tiling of images.

    magick *.webp -append merged.png
    magick *.webp +append merged.png

    AV files.

    $count = ((ls) -match '^\d+\.mp4$').count; $page = @(); $n=1; while ($n -ne $count+1) {$page += "file $n.mp4"; $n++}; $page | out-file -encoding ascii MERGE.txt; ffmpeg -f concat -safe 0 -i MERGE.txt -c copy OUTPUT.mp4
    (ls) -match '^\d+\.mp4' | del

    Add -fflags +igndts to ffmpeg options in case of Non-monotonous DTS in output stream error.

    Convert images to pdf, then merge them:

    (ls) -match '^\d+\.jpg' | forEach {magick $_.FullName ($_.FullName).Replace('jpg','pdf')}
    # Use any one
    pdftk ((ls) -match '^\d+\.pdf' | sort {($_.Name) -replace '\D+' -as [int]}) cat output merged.pdf
    pdftk ((ls) -match '^\d+\.pdf' | sort @{expression = {[int]$_.name.split('.')[0]}}) cat output merged.pdf
    pdftk ((ls) -match '^\d+\.pdf' | sort @{expression = {[int]$_.basename}}) cat output merged.pdf
    
    del ((ls) -match '^\d+\.(pdf|jpg)')
    
  • Rename files
    The $_.extension is helpful when dealing with similar files but different encodings (eg. BMP, JPG, GIF, PNG, SVG).

    $i=1; ls | forEach {ren $_.name "$i$($_.extension)"; $i++}

    Use regex to rename files.
    eg. sparse_dca,denoising_dca,variational_dca -> dca_denoising,dca_sparse,dca_variational

    (ls).Name | ren -NewName {$_ -replace '(\w+)_dca','dca_$1'}

Multimedia

  • Play in background (system tray)
    vlc --qt-start-minimized --play-and-exit $Music
  • Create & Play a playlist
    (ls *.mp4) | Sort-Object {(.Name) -replace '\D+' -as [int]} | forEach { vlc --one-instance --playlist-enqueue --rate 2.0 $_ }
    (ls *.mp3).fullName | forEach { vlc --one-instance --playlist-enqueue --rate 2.0 $_ }
  • Play music daily, at a specified time (7:30PM)
    Register-ScheduledJob
      -Name myDailyJobs
      -ScriptBlock {vlc $Music}
      -Trigger (New-JobTrigger -Daily -At '19:30')
      -ScheduledJobOption (New-ScheduledJobOption -WakeToRun -RunElevated)
  • Text to Speech
    See: SpeechSynthesizer
    Add-Type -AssemblyName System.Speech
    
    $tts = New-Object -TypeName System.Speech.Synthesis.SpeechSynthesizer
    $tts.SpeakAsync($(cat .\notes.txt))
    
    $tts = [System.Speech.Synthesis.SpeechSynthesizer]::new()
    $tts.SetOutputToWaveFile("$($pwd)\test.wav")
    $tts.Speak($(cat .\notes.txt))
    
    $tts.GetInstalledVoices().VoiceInfo
    $tts.SelectVoice("Microsoft Zira Desktop")
    $tts.SetOutputToWaveFile("$($pwd)\notes.wav")
    $tts.Speak($(cat .\notes.txt))
    ffmpeg -i notes.wav notes.mp3; del notes.wav;
    
    Remove-Variable tts
    $tts.SpeakAsync("The current date and time is $(Get-Date)")
    
    $services = Get-Service W* | Select-Object -First 5
    forEach ($service in $services){ `
        $tts.SpeakAsync("The Service $($service.displayname) is $($service.status)") | Out-Null `
    }

Add Context-Menu Options [BROKEN]

A context-menu opens up when you right click a file.
This adds an option Open This With ST3 to the context-menu for .ps1 files.

$ContextOption  = "Open This With ST3"
$ContextCommand = 'subl "%1"'

$baseKey = 'Registry::HKEY_CLASSES_ROOT\.ps1'
$id = (Get-ItemProperty $baseKey).'(Default)'
$ownId = $ContextOption.Replace(' ','')
$ContextOptionKey = "HKCU:\Software\Classes\$id\Shell\$ownId"
$ContextCommandKey = "$ContextOptionKey\Command"

New-Item -Path $ContextCommandKey -Value $ContextCommand -Force
Set-Item -Path $ContextOptionKey -Value $ContextOption

Location Access
To view physical location using GMaps, enable all location access features on Windows 10, then:

Add-Type -AssemblyName System.Device
$coordinates = [System.Device.Location.GeoCoordinateWatcher]::new()
$coordinates.Start()
$location = $coordinates.position.location
firefox "google.com/maps?q=$($location.latitude),$($location.longitude)"

Notifications
Use BurntToast to create toast-notifications. Use it with Register-ScheduledJob cmdlet to create non-volatile jobs.

Register-ScheduledJob -Name HourlyReminder
    -ScriptBlock {New-BurntToastNotification -Sound Call -Text "The time is $(Get-Date -Format 'hh:mm')"}
    -Trigger (New-JobTrigger -Once -At '6:00 AM' -RepetitionInterval (New-TimeSpan -Minutes 5) -RepetitionDuration ([TimeSpan]::MaxValue))
    -ScheduledJobOption (New-ScheduledJobOption -WakeToRun -RunElevated)

Windows

System Help

  • Update help.
    update-help -Force
    
  • Get help.
    man *service*
    man get-service
    man get-service -detailed
    man get-service -showwindow
    man get-service -examples
    man get-service -full
    man get-service -online
    man *eventlog*
    man about_*
    
  • Nouns & Verbs
    PS names cmdlets/functions in a Verb-Noun manner, to make their names predictable. (eg. Get-Service)
    To lookup all PERMITTED verbs (98), use:
    get-verb
    get-verb | measure
    
  • Write help for custom-cmdlets:
    You can write help in two ways: using comment-strings (<##>) within script files, or using XML files (<PSSnapInAssemblyName>.dll-Help.xml).

Variables

  • Single & Double Quotes (',")
    Anything within a single-quote is a literal string.
    Anything within a double-quote is a parsed string.

    PS> $var='LOCALHOST'
    PS> $var1='Try $var'
    PS> $var2="Try $var"
    PS> $var; $var1; $var2;
    LOCALHOST
    Try $var
    Try LOCALHOST
    

    Using double-quotes, you can also assign cmdlet & the object with it's property/method that it generates.

    PS> $services=get-service
    PS> $var="First Service Is $($services[0].name)"
    First Service Is ABLookupSvc
    
  • Escape Sequences
    Just as C-syntax uses back-slash (\) to escape sequences, PS-syntax used back-tick(`)

  • Accessing Object Property/Method
    PS3+ allows using Object.Property/Object.Method syntax.

    PS> $services=Get-Service
    PS> $services.name; $services.gettype()
    

    Here, Name & GetType() are property/method defined on objects returned by Get-Service. Check using gsv | gm.

  • Declaring Variable Types Use [type]$var notation.

    PS> [int]$var = Read-Host "Enter A Number"
    Enter A Number: 100
    PS> $var | gm
        TypeName: System.Int32
    ...
    
Type Description
[int],[single],[double] integer, single/double precision floating values
[char],[string] character, string of characters
[xml] XML document
[adsi] ActiveDirectory Service Interface (ADSI) query

Operators

Initialize with a range of values

Use .. operator to describe a range of integer values between any two values.

$x = 1..10; $y = 0.5..1

1 2 3 4 5
0 1

Run code within quotes ("...")

Quotes ("...") can be effectively used as formatted strings.
Use the $() operator to execute code (commands, scripts, and scriptblocks {...}) within a quoted expression.
$() returns a single value by default. It may return an array as well.

PS> "The date is $(Get-Date -Format 'dd-MM-yy hh:mm tt')"

The date is 26-06-20 11:34 AM

PS> "Files in this directory are: $((ls -File).Name -join ', ')"

Files in this directory are: README.md, scripts.md, test.ps1

Run external code

Use & & . operators to run code (commands, scripts, and scriptblocks {...}) from anywhere on your PC.
& runs the code in a new (temporary) scope. All changes made here are lost as soon as the execution is over.
. runs the code in current scope. All changes made here are retained in current scope (shell/script).
These are needed because a command like "B:\CRTejaswi\Codes\test.exe" is interpreted as a string.
Syntax: [&/.] "[path/to/file]" [arguments]

& "B:\CRTejaswi\Codes\test.exe" 2 10 "B:\CRTejaswi\Files\test.txt"
. "B:\CRTejaswi\Codes\test.exe" 2 10 "B:\CRTejaswi\Files\test.txt"

$x=1; $x # 1
& {$x = $x*10}; $x # 1
. {$x = $x*10}; $x # 10

This is handy when you just want to run something repeatedly and are too lazy to source it in an environment variable.

An important feature of . operator is that everything within the code's script is now available in the current scope.
eg. If you run a script . ./test.ps1; every variable/function/cmdlet in test.ps1 is now available in the Shell.

Control Flow

if-else
Conditional execution.
Execution is based on certain well-defined conditions.

  • Syntax

    if (<condition>){
        ...
    } elseif (<condition>){
        ...
    } else {
        ...
    }
    
  • Do something if today is Monday, post 12PM.

    $now = get-date
    if ($now.dayofweek -eq 'Monday' -and $now.hour -gt 12){
        ...
    }
    

switch
Conditional-Value execution.
Execution is based on certain properties/values of <object>.

  • Syntax

    switch (<object>){
        <case>  {...}
        <case>  {...}
        default {...}
    }
    
  • Check if a string contains '1' or 'a'

    $x = '123abc'
    switch -wildcard ($x){
        "*1*" {"Contains 1"}
        "*a*" {"Contains a"}
        default {"No matches found"}
    }
    

for
Iterative Conditional execution.

  • Syntax

    for (<start>; <condition>; <action>){
        ...
    }
    
  • Print the number of characters in a string iteratively (*actually prints indices)

    $x = '123abc'
    for ($i=0; $i -lt $x.Length; $i++){
        Write-Host $i -foreground black -background yellow
    }
    

forEach
Iterative execution, for each item in items.

  • Syntax

    forEach ($item in $items){
        ...
    }
    
  • Print the squares of 1-10

    $numbers = 1..10
    forEach ($number in $numbers){
        Write-Host ($number*$number)
    }
    

do-while
Execute until condition fails.

  • Syntax
    [1]: Execute only when condition starts to hold $True. (May not be executed at all.)

    while (<condition>){
        ...
    }
    

    [2]: Execute atleast once.

    do {
        ...
    } while (<condition>)
    
  • ...

    ...
    

break
Abort execution abruptly.

  • In a switch,for,forEach,or do-while construct, break exits the construct only.
  • In an if-else construct, break doesn't just exit the construct, but also the parent construct that contains the if-else.

NOTE
Avoid these mistakes:

  • Conditionally breaking from codeblock.
    [NO]
    while ($True){
        $choice = Read-Host 'Enter A Number'
        if ($choice -eq 0) { break }
    }
    
    [YES]
    do {
        $choice = Read-Host 'Enter A Number'
    } while ($choice -ne 0)
    

Data Structures

Array

  • Create an empty array

    $myArray = @(); $myArray.getType()
    
    IsPublic IsSerial Name                                     BaseType
    -------- -------- ----                                     --------
    True     True     Object[]                                 System.Array
  • Initialize an array
    An array can contain values of different datatypes:

    # Array (flexible type)
    $myArray = 1,'Two',"$PSHome"

    We can define fixed-type arrays as well:

    # Array (fixed type)
    [int[]] $myArray = 1,2,3
    • Example: Get process data
    $myArray = gps firefox,powershell,sublime_text
    $myArray.length # 11
  • Array of Arrays
    We can initialize an array of arrays (Jagged Array):

    # Array (array-of-arrays)
    $myArray = @(
        (1,2,3),
        (4,5),
        (6,7,8,9))
    $myArray[1][1] # 5

    We can initialize a matrix (Non-Jagged Array):

    # Array (fixed-size matrix)
    $myArray = New-Object "int32[,]" 3,3
    $myArray[1,1] = 5
    $myArray[1,1] # 5

    The two differ in that the former doesn't have fixed dimensions (depends on input arrays), but the latter does (3x3).

  • Access elements of an Array
    To access an element:

    $myArray = 1,'Two',"$PSHome"
    $myArray[1] # Two

    To access a range of elements (Array Slicing):

    $myArray = 1,'Two',"$PSHome"
    $myArray[1..2 +0 +1]
    
    Two C:\Windows\System32\WindowsPowerShell\v1.0 1 Two

    This accesses 2nd & 3rd values, then 0th & 1th value.
    To iterate through an array, use forEach:

    $myArray = 1..10; $sum = 0
    $myArray | forEach {$sum += $_}
    $sum # 56
  • Sort an array
    Use Sort-Object(aka sort).

  • Add/Remove elements to/from an array
    To add, simply use +=.

    $myArray = 1..10
    $myArray += 11,12,13
    $myArray # 1 2 3 4 5 6 7 8 9 10 11 12 13

    To remove, use conditional-operator or regex (-ne,-notlike,-notmatch) to describe the elements (to remove), and store the result back.

    $myArray = 1..13
    $myArray = $myArray -notmatch '1.'
    $myArray # 1 2 3 4 5 6 7 8 9
  • Working with dynamic arrays
    Use .NET's System.Collections.ArrayList class to work with dynamic arrays.
    This is useful when you are frequently manipulating an array with large amounts of data.

    $myArray = New-Object System.Collections.ArrayList

Associative Array (Hash Table)

  • Create an empty hashtable

    $myHash = @{}; $myHash.getType()
    
    IsPublic IsSerial Name                                     BaseType
    -------- -------- ----                                     --------
    True     True     Hashtable                                System.Object
  • Initialize a hashtable
    We can initialize a hashtable using key=value pairs:

    $myHash = @{'Name'='Chaitanya'; 'Directory'="$PSHome"}
    $myHash
    
    Name                           Value
    ----                           -----
    Name                           Chaitanya
    Directory                      C:\Windows\System32\WindowsPowerShell\v1.0

    We can initialize a hashtable using key incrementally:

    $myHash = @{}; $myHash['Name']='Chaitanya'; $myHash.Directory="$PSHome"
    $myHash
    
    Name                           Value
    ----                           -----
    Name                           Chaitanya
    Directory                      C:\Windows\System32\WindowsPowerShell\v1.0
  • Sort a hashtable
    Use .GetEnumerator() method to access each element. Sort this by name/value to sort by key/value.

    $myHash = @{}; $myHash['Name']='Chaitanya'; $myHash.Veto='Alpha'
    $myHash.GetEnumerator() | sort name
    
    Name                           Value
    ----                           -----
    Name                           Chaitanya
    Veto                           Alpha
    
    
    $myHash.GetEnumerator() | sort value
    
    Name                           Value
    ----                           -----
    Veto                           Alpha
    Name                           Chaitanya

NOTE: Following are some strongly-typed 'Generic' .NET data structures. These can be useful when you want to constrain the variables to fixed datatypes.
See System.Collections.Generic for documentation.

List

  • Create an empty list

    $myList = New-Object System.Collections.Generic.List[String]
    $myList = [System.Collections.Generic.List[String]]::new()

    This syntax is better for use:

    $myList = [System.Collections.Generic.List[String]]("Apple","Ball","Cat")
    $myList = [System.Collections.Generic.List[String]](cat $myLinks)
  • Add/Remove elements to/from a list
    PS offers 4 add & remove methods each.

    add/addRange         -> append one/many entries at the end of list.
    insert/insertRange   -> insert one/many entries at specific index.
    
    remove               -> remove one entry (by value)
    removeAt/removeRange -> remove one/many entries (by index) from a specific index.
    removeAll            -> remove entries based on a criteria.
    

    We can use ScriptBlock ({}) within removeAll() to define criteria.
    This example makes use of param($x) to define a lambda function

    $myList.add("Ball")
    $myList.addRange([String[]]("Cat","Dog","Ear"))
    $myList # Ball Cat Dog Ear
    $myList.insert(0,"Apple")
    $myList.insertRange(5,[String[]]("Fish","Girl","Ice"))
    $myList # Apple Ball Cat Dog Ear Fish Girl Ice
    
    $myList.remove("Ice") # True <- value
    $myList # Apple Ball Cat Dog Ear Fish Girl
    $myList.removeAt(6) # <- index
    $myList # Apple Ball Cat Dog Ear Fish
    $myList.removeRange(3,2) # True <- index,count
    $myList # Apple Ball Cat Fish
    $myList.removeAll({param($x) $x -match "^f."}) # 1 <- count
    $myList # Apple Ball Cat
  • Select elements from a list

    $myList[1] # Bat
    $myList.indexOf('Bat') # 1
  • Modify elements in a list

    $myList[1] = 'Bat'
    $myList # Apple Bat Cat
  • Search/Sort a list
    Alike removeAll(), we can use ScriptBlock ({}) within findAll() to define criteria.
    binarySearch() implements Binary Search to iteratively shrink dataset. This is prefered over indexOf() working with extremely large number of values.

    $myList.findAll({param($x) $x -match "^a."}) # 1 <- count
    # $myList.sort() ??
    # $myList.binarySearch() ??
    $myList = [System.Collections.Generic.List[Int]](1..1000000)
    $myList.binarySearch(1024); $myList.indexOf(1024); # 1023 1023

Dictionary

  • Create an empty dictionary

    $myDict = New-Object System.Collections.Generic.Dictionary"[String,String]"
    $myDict = [System.Collections.Generic.Dictionary[String,String]]::new()

    This syntax is better for use: [BROKEN]

    # $myDict = [System.Collections.Generic.Dictionary[String,String]](("Name","Adam")("Group","Alpha")("Grade","A"))
  • Add/Remove elements to/from a dictionary

    $myDict.add("Name","Chaitanya"); $myDict.Directory = "$PSHome"; $myDict.Age = 23;
    $myDict
    
    Key       Value
    ---       -----
    Name      Chaitanya
    Directory C:\Windows\System32\WindowsPowerShell\v1.0
    Age       23
    
    $myDict.remove('Age')
    $myDict
    
    Key       Value
    ---       -----
    Name      Chaitanya
    Directory C:\Windows\System32\WindowsPowerShell\v1.0
  • Select elements from a dictionary

    $myDict['Name']; $myDict.Name; # Chaitanya Chaitanya
  • Modify elements in a dictionary

    $myDict['Name']='Tejaswi'; $myDict.Name; # Tejaswi
    $myDict.Name='Chaitanya'; $myDict.Name;  # Chaitanya
  • Check for key/value in a dictionary

    $myDict.ContainsKey('Name'); $myDict.ContainsValue('Chaitanya'); # True True
    • Add a key/value pair to an existing dictionary
    if (-not $myDict.containsKey('Age')){
        $myDict.add('Age',23)
    }
    $myDict.Age # 23
  • Enumerate a dictionary

    $myDict.Keys; $myDict.values;
    # Name Directory Age
    # Chaitanya C:\Windows\System32\WindowsPowerShell\v1.0 23

Stack

  • Create an empty stack

    $myStack = New-Object System.Collections.Generic.Stack[String]
    $myStack = [System.Collections.Generic.Stack[String]]::new()

    This syntax is better for use:

    $myStack = [System.Collections.Generic.Stack[String]]("Apple","Ball","Cat")
    $myStack = [System.Collections.Generic.Stack[String]](cat $myLinks)
  • Add/Remove elements to/from a stack

    $myStack.push("Apple"); $myStack.push("Ball"); $myStack.push("Cat");
    $myStack.pop() # Cat
  • Enumerate a stack We can peek at top-of-stack using .peek(). To index a stack, convert it to array using .toArray()

    $myStack = [System.Collections.Generic.Stack[String]]('Apple','Ball','Cat')
    $myStack.peek() # Cat
    $myArray = $myStack.toArray() # Cat Ball Apple

Queue

  • Create an empty queue

    $myQueue = New-Object System.Collections.Generic.Queue[String]
    $myQueue = [System.Collections.Generic.Queue[String]]::new()

    This syntax is better for use:

    $myQueue = [System.Collections.Generic.Queue[String]]("Apple","Ball","Cat")
    $myQueue = [System.Collections.Generic.Queue[String]](cat $myLinks)
  • Add/Remove elements to/from a queue

    $myQueue.enqueue("Apple"); $myQueue.enqueue("Ball"); $myQueue.enqueue("Cat");
    $myQueue.dequeue() # Apple
  • Enumerate a queue We can peek at top-of-queue using .peek(). To index a queue, convert it to array using .toArray()

    $myQueue = [System.Collections.Generic.Queue[String]]('Apple','Ball','Cat')
    $myQueue.peek() # Apple
    $myArray = $myQueue.toArray() # Apple Ball Cat

Objects

Unlike UNIX, PS outputs objects (instead of text). This makes it easy to sort them.
This instruction gets all the members (properties/methods) of get-process cmdlet, sorted by name.

get-process | get-member | sort Name

       TypeName: System.Diagnostics.Process

    Name                       MemberType     Definition
    ----                       ----------     ----------
    __NounName                 NoteProperty   string __NounName=Process
    BasePriority               Property       int BasePriority {get;}
    BeginErrorReadLine         Method         void BeginErrorReadLine()
    BeginOutputReadLine        Method         void BeginOutputReadLine()
    CancelErrorRead            Method         void CancelErrorRead()
    CancelOutputRead           Method         void CancelOutputRead()
    Close                      Method         void Close()
    CloseMainWindow            Method         bool CloseMainWindow()
    Company                    ScriptProperty System.Object Company {get=$this.Mainmodule.FileVersionInfo.CompanyName;}
    Container                  Property       System.ComponentModel.IContainer Container {get;}
    CPU                        ScriptProperty System.Object CPU {get=$this.TotalProcessorTime.TotalSeconds;}
    CreateObjRef               Method         System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)
    Description                ScriptProperty System.Object Description {get=$this.Mainmodule.FileVersionInfo.FileDescription;}
    Dispose                    Method         void Dispose(), void IDisposable.Dispose()
    Disposed                   Event          System.EventHandler Disposed(System.Object, System.EventArgs)
    EnableRaisingEvents        Property       bool EnableRaisingEvents {get;set;}
    Equals                     Method         bool Equals(System.Object obj)
    ErrorDataReceived          Event          System.Diagnostics.DataReceivedEventHandler ErrorDataReceived(System.Object, System.Diagnostics.DataReceivedEventArgs)
    ExitCode                   Property       int ExitCode {get;}
    Exited                     Event          System.EventHandler Exited(System.Object, System.EventArgs)
    ExitTime                   Property       datetime ExitTime {get;}
    FileVersion                ScriptProperty System.Object FileVersion {get=$this.Mainmodule.FileVersionInfo.FileVersion;}
    GetHashCode                Method         int GetHashCode()
    GetLifetimeService         Method         System.Object GetLifetimeService()
    GetType                    Method         type GetType()
    Handle                     Property       System.IntPtr Handle {get;}
    HandleCount                Property       int HandleCount {get;}
    Handles                    AliasProperty  Handles = Handlecount
    HasExited                  Property       bool HasExited {get;}
    Id                         Property       int Id {get;}
    InitializeLifetimeService  Method         System.Object InitializeLifetimeService()
    Kill                       Method         void Kill()
    MachineName                Property       string MachineName {get;}
    MainModule                 Property       System.Diagnostics.ProcessModule MainModule {get;}
    MainWindowHandle           Property       System.IntPtr MainWindowHandle {get;}
    MainWindowTitle            Property       string MainWindowTitle {get;}
    MaxWorkingSet              Property       System.IntPtr MaxWorkingSet {get;set;}
    MinWorkingSet              Property       System.IntPtr MinWorkingSet {get;set;}
    Modules                    Property       System.Diagnostics.ProcessModuleCollection Modules {get;}
    Name                       AliasProperty  Name = ProcessName
    NonpagedSystemMemorySize   Property       int NonpagedSystemMemorySize {get;}
    NonpagedSystemMemorySize64 Property       long NonpagedSystemMemorySize64 {get;}
    NPM                        AliasProperty  NPM = NonpagedSystemMemorySize64
    OutputDataReceived         Event          System.Diagnostics.DataReceivedEventHandler OutputDataReceived(System.Object, System.Diagnostics.DataReceivedEventArgs)
    PagedMemorySize            Property       int PagedMemorySize {get;}
    PagedMemorySize64          Property       long PagedMemorySize64 {get;}
    PagedSystemMemorySize      Property       int PagedSystemMemorySize {get;}
    PagedSystemMemorySize64    Property       long PagedSystemMemorySize64 {get;}
    Path                       ScriptProperty System.Object Path {get=$this.Mainmodule.FileName;}
    PeakPagedMemorySize        Property       int PeakPagedMemorySize {get;}
    PeakPagedMemorySize64      Property       long PeakPagedMemorySize64 {get;}
    PeakVirtualMemorySize      Property       int PeakVirtualMemorySize {get;}
    PeakVirtualMemorySize64    Property       long PeakVirtualMemorySize64 {get;}
    PeakWorkingSet             Property       int PeakWorkingSet {get;}
    PeakWorkingSet64           Property       long PeakWorkingSet64 {get;}
    PM                         AliasProperty  PM = PagedMemorySize64
    PriorityBoostEnabled       Property       bool PriorityBoostEnabled {get;set;}
    PriorityClass              Property       System.Diagnostics.ProcessPriorityClass PriorityClass {get;set;}
    PrivateMemorySize          Property       int PrivateMemorySize {get;}
    PrivateMemorySize64        Property       long PrivateMemorySize64 {get;}
    PrivilegedProcessorTime    Property       timespan PrivilegedProcessorTime {get;}
    ProcessName                Property       string ProcessName {get;}
    ProcessorAffinity          Property       System.IntPtr ProcessorAffinity {get;set;}
    Product                    ScriptProperty System.Object Product {get=$this.Mainmodule.FileVersionInfo.ProductName;}
    ProductVersion             ScriptProperty System.Object ProductVersion {get=$this.Mainmodule.FileVersionInfo.ProductVersion;}
    PSConfiguration            PropertySet    PSConfiguration {Name, Id, PriorityClass, FileVersion}
    PSResources                PropertySet    PSResources {Name, Id, Handlecount, WorkingSet, NonPagedMemorySize, PagedMemorySize, PrivateMemorySize, VirtualMemorySize, Threads.Count, TotalProcessorTime}
    Refresh                    Method         void Refresh()
    Responding                 Property       bool Responding {get;}
    SafeHandle                 Property       Microsoft.Win32.SafeHandles.SafeProcessHandle SafeHandle {get;}
    SessionId                  Property       int SessionId {get;}
    SI                         AliasProperty  SI = SessionId
    Site                       Property       System.ComponentModel.ISite Site {get;set;}
    StandardError              Property       System.IO.StreamReader StandardError {get;}
    StandardInput              Property       System.IO.StreamWriter StandardInput {get;}
    StandardOutput             Property       System.IO.StreamReader StandardOutput {get;}
    Start                      Method         bool Start()
    StartInfo                  Property       System.Diagnostics.ProcessStartInfo StartInfo {get;set;}
    StartTime                  Property       datetime StartTime {get;}
    SynchronizingObject        Property       System.ComponentModel.ISynchronizeInvoke SynchronizingObject {get;set;}
    Threads                    Property       System.Diagnostics.ProcessThreadCollection Threads {get;}
    ToString                   Method         string ToString()
    TotalProcessorTime         Property       timespan TotalProcessorTime {get;}
    UserProcessorTime          Property       timespan UserProcessorTime {get;}
    VirtualMemorySize          Property       int VirtualMemorySize {get;}
    VirtualMemorySize64        Property       long VirtualMemorySize64 {get;}
    VM                         AliasProperty  VM = VirtualMemorySize64
    WaitForExit                Method         bool WaitForExit(int milliseconds), void WaitForExit()
    WaitForInputIdle           Method         bool WaitForInputIdle(int milliseconds), bool WaitForInputIdle()
    WorkingSet                 Property       int WorkingSet {get;}
    WorkingSet64               Property       long WorkingSet64 {get;}
    WS                         AliasProperty  WS = WorkingSet64
  • Objects: Sorting & Selecting
    Use Sort-Object (sort) & Select-Object(select).
    # Sort processes based on ID & VM-usage (descending). Display only ID, Name, VM, PM.
    gps | select ID,Name,VM,PM | sort VM,ID -desc
    # Save above table as HTML.
    gps | select ID,Name,VM,PM | sort VM,ID -desc | convertto-html | out-file TEST.html
    

Group-Object
We can group objects based on like values/properties.

$Object | Group-Object <switch>
  • Group email addresses by provider
    $emails = @('abc@gmail.com', 'xyz@gmail.com', 'abc@yahoo.com', 'abc@yahoo.com')
    $emails | group { ($_ -split '@')[1] }
    
    Here, -split splits result into two parts, accessible using [0] & [1].

Measure-Object
We can perfom certain arithmetic operations on objects.

$Object | Measure-Object <switch>
Type Switch Meaning
Numeric -Max, -Min, -Average max/min & average of the values
String -Char, -Word, -Line #chars, #words, #lines in string

Compare-Object
We can compare entries within two objects.

Compare-Object -reference  $A -difference $B -includeEqual -excludeDifferent
Compare-Object -reference 1,2,3,4 -difference 1,2 -includeEqual

InputObject SideIndicator
----------- -------------
          1 ==
          2 ==
          3 <=
          4 <=
  • Compare two folders; return files with same name & size.

    $ref = ls ('C:\Windows\System32' -file); $diff = (ls 'C:\Windows\SysWOW64' -file);
    compare = -ref $ref -diff $diff -property Name,Length -includeEqual -excludeDifferent
    
    Name                              Length SideIndicator
    ----                              ------ -------------
    @AppHelpToast.png                    232 ==
    @AudioToastIcon.png                  308 ==
    @EnrollmentToastIcon.png             330 ==
    @VpnToastIcon.png                    404 ==
    ...
  • Objects: Selecting (Sort-Object v Where-Object)
    Sort-Object lets you select/filter objects based on properties.
    Where-Object lets you select/filter objects based on a criteria.

    # List all processes with ID >= 1000, sorted in ascending order.
    gps | where {$_.Handles -ge 1000} | sort -Property Handles
    
    Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
    -------  ------    -----      -----     ------     --  -- -----------
       1011      42    39560      23564              4664   0 LenovoVantageService
       1060      22    11932      24380              1044   0 svchost
       1073       6     8740      12100             14168   0 Windows.WARP.JITService
       1136      52    50488      41276              4136   0 Lenovo.Modern.ImController
       1201      17     7516      11760              1208   0 svchost
       1209      91   102380     165036      18.14   8500   1 SearchUI
       1376      22     7248      13288               976   0 lsass
       1453      93   179728     231288      42.13   3016   1 firefox
       2075      81   384764     151604              4332   0 MsMpEng
       3523       9     1868       3972              8632   1 rundll32
       3538     137   119552     132228     153.25   7980   1 explorer
       5508       0      188        136                 4   0 System
    
TODO
  • Study ls,sort,select,where cmdlets.

  • Get current date & time. Then, show only time.

get-date; get-date | select Hour,Minute,Second

Saturday, May 9, 2020 11:49:03 AM

Hour   : 11
Minute : 49
Second : 3

  • Display a list of installed hotfixes. Display installation date, installed by, and ID, sorted by installation date.

    get-hotfix | select installedon,installedby,hotfixid | sort installedon
    
    InstalledOn           installedby         hotfixid
    -----------           -----------         --------
    3/13/2020 12:00:00 AM NT AUTHORITY\SYSTEM KB4538674
    3/13/2020 12:00:00 AM NT AUTHORITY\SYSTEM KB4541338
    3/13/2020 12:00:00 AM NT AUTHORITY\SYSTEM KB4537759
    3/13/2020 12:00:00 AM NT AUTHORITY\SYSTEM KB4537572
    3/13/2020 12:00:00 AM NT AUTHORITY\SYSTEM KB4517245
    4/22/2020 12:00:00 AM NT AUTHORITY\SYSTEM KB4549951
    4/22/2020 12:00:00 AM NT AUTHORITY\SYSTEM KB4552152
    
  • Display a list of 10 latest Security Event-logs. Display index, time, source for each file, with the oldest entries appearing first (and same-time entries sorted by index).

    get-eventlog -logname system -newest 10 | select index,timegenerated,source | sort timegenerated,index | out-gridview
    
    Index TimeGenerated        Source
    ----- -------------        ------
    19167 5/9/2020 10:09:26 AM Microsoft-Windows-TPM-WMI
    19168 5/9/2020 10:09:26 AM Microsoft-Windows-Winlogon
    19169 5/9/2020 10:09:27 AM Microsoft-Windows-TPM-WMI
    19170 5/9/2020 10:11:22 AM DCOM
    19171 5/9/2020 10:11:22 AM DCOM
    19172 5/9/2020 10:11:22 AM DCOM
    19173 5/9/2020 10:11:25 AM Microsoft-Windows-FilterManager
    19174 5/9/2020 10:15:16 AM Service Control Manager
    19175 5/9/2020 10:17:21 AM Service Control Manager
    19176 5/9/2020 10:19:17 AM Microsoft-Windows-Kernel-General
    

Pipelining

  • Import/Export

    man export*
    man import*
    
    Export-Clixml                     Cmdlet
    Export-Csv                        Cmdlet
    Export-FormatData                 Cmdlet
    Export-PSSession                  Cmdlet
    Export-BinaryMiLog                Cmdlet
    Export-WindowsDriver              Cmdlet
    Export-WindowsCapabilitySource    Cmdlet
    Export-WindowsImage               Cmdlet
    Export-Counter                    Cmdlet
    Export-ODataEndpointProxy         Function
    Export-Certificate                Cmdlet
    Export-PfxCertificate             Cmdlet
    Export-ProvisioningPackage        Cmdlet
    Export-Trace                      Cmdlet
    Export-ScheduledTask              Function
    Export-StartLayoutEdgeAssets      Cmdlet
    Export-StartLayout                Cmdlet
    Export-TlsSessionTicketKey        Cmdlet
    
    ImportSystemModules               Function
    Import-PowerShellDataFile         Function
    Import-Module                     Cmdlet
    Import-Alias                      Cmdlet
    Import-Clixml                     Cmdlet
    Import-Csv                        Cmdlet
    Import-LocalizedData              Cmdlet
    Import-PSSession                  Cmdlet
    Import-PackageProvider            Cmdlet
    Import-BinaryMiLog                Cmdlet
    Import-IseSnippet                 Function
    Import-Counter                    Cmdlet
    Import-PfxCertificate             Cmdlet
    Import-Certificate                Cmdlet
    Import-StartLayout                Cmdlet
    Import-TpmOwnerAuth               Cmdlet
    
  • Import/Export CSV, XML

    gps | export-csv process.csv
    gps | export-clixml process.xml
    
  • Convert to HTML

    gps | convertto-html | out-file TEST.html
    ls  | convertto-html | out-file -append TEST.html
    
TODO
  • Select/Sort & save necessary columns to TEST.html

  • Using CSS, beautiful TEST.html

  • Compare two structured files (XML)

    # List different processes running in two PCs (reference, difference).
    diff -reference (import-clixml reference.xml) -difference (gps) -property Name
    
    name         SideIndicator
    ----         -------------
    calc            =>
    mspaint         =>
    notepad         <=
    
  • Output

    out-file     -> O/P to file.
    out-gridview -> O/P to table in new GUI window. (*)
    out-printer  -> O/P to printer (SaveAs PDF).
    

    Instead of piping > output to a file, use out-file.
    out-file lets you specify:

    -append   -> append, not replace contents
    -encoding -> file encoding (ascii, unicode, utf8, oem, string)
    -width    -> set column width (default = 80 characters/line)
    
    # Append outputs instead of replacing.
    ls | out-file -append -width 100 TEST.txt
    

Pipeline Parameter-Binding

Given: cmdA | cmdB; What exactly goes through the |?
Ans: Objects

  • Display processes/services from a list of computers connected to your PC.
    # computers.csv
    hostname,operatingsystem
    Feynman,windows
    ...
    
    # Processes
    gps -ComputerName (import-csv .\computers.csv | select -ExpandProperty hostname) | out-gridview
    
    # Services
    gsv -ComputerName (import-csv .\computers.csv | select -ExpandProperty hostname) | select Name,Status | sort Name | out-gridview
    

Item Access

File/Folder are collectively called Item.

  • Current location
    The Get-Location (aka pwd) gets current directory path. If you are working with multiple locations, you can push/pop those locations to/from a stack using Push-Location/Pop-Location (aka pushd/popd).

    # Get current location; Save current location to stack & move to another; Goto previous location (stored on stack)
    pwd; push 'B:\PowerShell'; popd
  • List items

    -Force displays hidden items as well; -Recurse recursively lists items.

    Get-ChildItem -Path C:\ -Force
    ls C:\ -Force
    • Find all binaries within Program Files that were last modified after October 1, 2005 and size is 1-10MB
    ls $env:ProgramFiles *.exe -Recurse |
        where {($_.LastWriteTime -gt '2005-10-01') -and ($_.Length -ge 1mb) -and ($_.Length -le 10mb)} |
        select Name,@{name='LastWriteTime';expression={Get-Date -Format 'dd-MM-yy hh:mm:ss tt' $_.LastWriteTime}}

File Metadata

See: Properties based on File-Type

$shell = new-object -com shell.application
$folder = $shell.namespace((pwd).Path)
$file = $folder.Items().Item('01.mp4')
$folder | gm; $file | gm;

Formatting

  • Format-Table, Format-List, Format-Wide, Format-Custom

  • Customized Formatting

  • Display process names, IDs, responding (to Windows or not) in a table

    gps | format-table Name,ID,Responding -autosize -wrap
    
  • Display process names, IDs, virtual/physical memory usage (in MB) in a table

    gps |
        format-table Name,ID,
        @{name='Virtual(MB)';expression={$_.vm/1MB};formatstring='F2'},
        @{name='Physical(MB)';expression={$_.workingset/1MB};formatstring='F2'} -autosize
    
  • Display available event-logs - their names and retention periods in a table

    get-eventlog -list |
        format-table @{name='Name';expression={$_.LogDisplayName}},
                     @{name='Retention(days)';expression={$_.MininumRetentionDays}}
    
  • Display service grouped-by their status (start/stop)
    [OUTPUT]

    gsv | sort status -desc | format-table -groupby status
    
  • Display a list of all binaries (.exe) in C:\Windows with their name, versionInfo & fileSize
    [OUTPUT]

    ls C:\Windows\*.exe | format-list Name,VersionInfo,@{Name='Size';Expression={$_.length}}
    

NOTE

  • Prefer outputting Objects over formatted-tables.
    [NO]
    gwmi win32_logicaldisk |
        format-table deviceid,
        @{name='Total Memory (GB)';e={$_.size/1GB};formatstring='F2'},
        @{name='Free Memory (GB)';e={$_.freespace/1GB};formatstring='F2'}
    
    [YES]
    gwmi win32_logicaldisk |
        select deviceid,
        @{name='Total Memory (GB)';e={$_.size/1GB -as [int]}},
        @{name='Free Memory (GB)';e={$_.freespace/1GB -as [int]}}
    
    Here we've used -as [int] to round off, as Select-Object doesn't offer formatstring option.
    Outputting objects allows us to do this (something which formatted tables don't):
    .\test.ps1 | export-csv test.csv
    

Filtering & Comparisons

The Approach

  • A. Specify what you need.

  • B. Filter out what you need from what PS gives you.

    eg. To get a specific service, you can specify it with get-service.

    gsv -name e*,*seo*
    

    But if you want a list of ONLY running services, you have to filter.

    gsv -name e*,*seo* | where {$_.status -eq 'running'}
    

    Don't rely on headers O/P by cmdlets like get-service to infer it's properties.
    Always use get-member to lookup cmdlet property names.

    gsv | gm
    

The Operators
Refer about_comparison_operators.

Name Description Note
-eq,-ceq equal to c implies case-sensitive
-ne,-cne not equal to .
-ge,-cge,-le,-cle greater/lesser than OR equal to .
-gt,-cgt,-lt,-clt greater/lesser than .
-and,-or,-not and, or, not .
-like,-clike checks matching strings c implies case-sensitive
-match,-cmatch matches RegEx .
  • Example (-not): Both these commands check if the process isn't responding.

    $_.responding -eq $False
    -not $_.responding
    
  • Display all binaries (EXE) in C:\Windows\System32 larger than 5MB.

    ls C:\Windows\System32\*.exe | where {$_.length -gt 5MB}
    
  • Get all hotfixes that are security updates.

    get-hotfix -Description 'Security Update'
    
  • Display a list of all running processes with the name Conhost or Svchost.

    gps -name svchost,conhost | format-table -groupby name
    

Background Processes (aka job)

PS calls 'background process', a job.

  • Create a local background process (job)
    Use [1] for on-spot instructions. Use [2] if instructions are retrieved from a file.

    start-job -scriptblock {...}
    start-job -filepath ...
    

    Although this can also work for remote execution, prefer WMI cmdlets.

    start-job -scriptblock {get-eventlog security -computerName server1,server2}
    
  • Create a remote background process (job)
    By default, WMI cmdlets, even if they're running on multiple PCs, do so synchronously.
    This means, if PC1 has a long list of commands to execute, it will take forever to execute commands on PC2.
    To do this asynchronously using background process (job), attach the -asJob parameter to the cmdlet.

    gwmi Win32_OperatingSystem -computerName (cat allservers.txt) -asJob
    
  • Managing background processes (job)

    Get-Job     -> Retrieve status of all jobs.
    Receive-Job -> Retrieve results of all jobs.
    Remove-Job  -> Deletes job alongwith all cached memory.
    Stop-Job    -> Terminates a blocked job.
    Wait-Job    -> Forces Shell to wait until a job is finished executing.
    

    Retrieving results of a job removes it from memory. To avoid this deletion, use -keep parameter.

    receive-job -id 1 -keep
    
  • Scheduling background processes (job)
    Start by creating a trigger using New-JobTrigger.
    Set additional options using New-ScheduledTaskOption.
    Register the job with the Task Scheduler using Register-ScheduledJob.
    The last step defines the job in 'Task Scheduler' by writing to an XML file.
    For me, this is located at C:\Users\Chaitanya Tejaswi\AppData\Local\Microsoft\Windows\PowerShell\ScheduledJobs.

  • Play an audio daily at 7:30PM

    register-scheduledjob -name myDailyJobs -scriptblock {vlc "B:\CRTejaswi\Music\Anna McLuckie - Little Man On The Moon.mp3"} -trigger (new-jobtrigger -daily -at '19:30')
    

    While this isn't neccesary for this particular example, use this template to run with elevated privileges.

    register-scheduledjob
      -name myDailyJobs
      -scriptblock {vlc "B:\CRTejaswi\Music\Anna McLuckie - Little Man On The Moon.mp3"} -trigger (new-jobtrigger -daily -at '19:30')
      -scheduledjoboption (new-scheduledjoboption -waketorun -runelevated)
    

    Scheduled tasks are non-volatile, meaning they don't get erased when you close the Shell. This is because it is stored in XML files on disk (C:\Users\Chaitanya Tejaswi\AppData\Local\Microsoft\Windows\PowerShell\ScheduledJobs).

Batch Cmdlets/Processing

Regular Expressions

Pattern matching is done using -match&-cmatch operators, and select-string cmdlet.

Expression Meaning
. Any character (except newline)
\d, \D Digit (0-9), !Digit
\w, \W Word (a-z, A-Z, 0-9, _), !Word
\s, \S Whitespace (space, tab, newline), !Whitespace
\b, \B WordBoundary, !WordBoundary
^, $ Beginning/End of String
[],[^],(),{} CharacterSet, !CharacterSet, WordGroup, #Values

Quantifiers

Expression Meaning
0 >=0
+ >=1
? 0 or 1
{n} Exact value (=n)
{start, stop} Range of values (min, max)
  • Get all files that have a 2-digit value in their name.
    ls | where {$_.name -match "\d{2}"}
    
  • Display process id, name & company of all processes that are from MicroSoft.
    gps | where {$_.company -match "^Microsoft"} | select name,id,company
    
  • Retrieve all DNS entries where Data property is an IPv4 address.
    Get-DnsClientCache | where {$_.data -match "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"}
    
  • Retrieve IIS logfile records that contain 40x errors.
    ls *.log -recurse |
        select-string -pattern "\s40[0-9]\s" |
        format-table Filename,LineNumber,Line -wrap
    

Remote Access

Refer: Get-WmiObject (gwmi), Get-CimInstance (gcim)

  • Windows Remote Management (WinRM)
  • Windows Management Instrumentation (WMI) [OLD] & the Common Information Model (CIM) [NEW]
    Get-WmiObject,Invoke-WmiMethod,GetCimInstance,Invoke-CimMethod
    WMI commands work over RPCs. CIM commands work over WS-MAN (WinRM).
    CIM instructions need WinRM to be enabled on every PC. So, prefer the old WMI instructions.
  • Classes
win32_logicaldisk:
win32_process:
win32_service:
win32_bios:
  • Display all HDD partitions with total/free memory.
    gwmi win32_logicaldisk |
        format-table deviceid,
        @{name='Total Memory (GB)';e={$_.size/1GB};formatstring='F2'},
        @{name='Free Memory (GB)';e={$_.freespace/1GB};formatstring='F2'}
    
  • Display list of services sorted by mode (auto, manual)
    gwmi -class win32_service -Filter "state = 'running'" | sort startmode,name
    

Providers

Refer: Providers
Also See: Registry

A provider represents a data storage as a filesystem.
Windows FileSystem has this heirarchy:

File -> Folder -> Drive

File/Folder are collectively called Item, and so we have items associated with a drive. Since a Provider maps physical storage to drives (even if they're not a filesystem object, eg. Registry): we have cmdlets that deal with items.
These verbs apply to all items:

Get/Set, Copy/Move, New/Remove/Rename/Clear

Each provider has certain 'capabilities' for its associated cmdlets.

ShouldProcess -> We can use `-WhatIf` & `-Confirm` to test before executing it.
Filter        -> We can use `-Filter` to filter out content.
Credentials   -> We can specify credentials to be provided each time a cmdlet is run.
Transactions  -> We can commit or rollback changes made by the cmdlet.

Built-In Providers

Provider Drive(s) OutputType Capabilities
Alias Alias: System.Management.Automation.AliasInfo ShouldProcess
Certificate Cert: Microsoft.PowerShell.Commands.X509StoreLocation ?
System.Security.Cryptography.X509Certificates.X509Certificate2 .
Environment Env: System.Collections.DictionaryEntry ShouldProcess
FileSystem C: System.IO.FileInfo ShouldProcess, Filter, Credentials
System.IO.DirectoryInfo .
Function Function: System.Management.Automation.FunctionInfo ShouldProcess
Registry HKLM: HKCU: Microsoft.Win32.RegistryKey ShouldProcess, Transactions
Variable Variable: System.Management.Automation.PSVariable ShouldProcess
WSMan WSMan: Microsoft.WSMan.Management.WSManConfigContainerElement Credentials

Registry

Refer: Registry

Registry is a database that stores low-level settings for any Windows OS.
Registry files are stored in %SystemRoot%\System32\Config

Structured Data (CSV, JSON, XML)

Refer: Talk (Jeff Hicks) + Files, Talk (Jason Horner)
Also See: myScripts

NOTE
All file conversion cmdlets use verbs like Import,Export,ConvertTo,ConvertFrom.
These have specific meaning. Import/Export -> convert & save to a file. ConvertTo/ConvertFrom -> only convert (to be displayed or piped further).

CSV

We use Import-Csv & Export-Csv to read from & write to CSV files.

  • Create new aliases
    Create aliases.csv

    Name,Value
    gotodir,Get-ChildItem
    sel,Select-Object
    clip,Get-Clipboard
    

    Import these aliases using:

    import-csv aliases.csv | new-alias
  • CSV files are all strings
    When you export objects to CSV, you lose type (end up having only Strings).
    Cast each header as a specific type.

    import-csv .\test.csv | forEach {
        [PSCustomObject]@{
            DeviceID = $_.DeviceID
            'Total Memory (GB)' = $_.'Total Memory (GB)' -as [int32]
            'Free Memory (GB)' = $_.'Free Memory (GB)' -as [int32]
        }
    } | gm
    
    Name              MemberType   Definition
    ----              ----------   ----------
    Equals            Method       bool Equals(System.Object obj)
    GetHashCode       Method       int GetHashCode()
    GetType           Method       type GetType()
    ToString          Method       string ToString()
    DeviceID          NoteProperty string DeviceID=A:
    Free Memory (GB)  NoteProperty int Free Memory (GB)=250
    Total Memory (GB) NoteProperty int Total Memory (GB)=250
  • Create a CSV marksheet for 5 students containing Name & Marks in Science, Maths, English, Hindi & ComputerScience. Import file, add TotalMarks & Percentage columns, then append them to file.

JSON

We use ConvertFrom-Json & ConvertTo-Json to read from & write to JSON objects.
Since these aren't Import & Export cmdlets, we have to use Out-File to write to file.
Use -depth n to define the depth to which you want to parse the file. The default is 2. This is useful to know if you have a file with several levels of heirarchy.

  • Create a JSON file
    Wrap raw JSON data inside literal-strings '...'.

    $json = '
    [
        {
            "Country": "India",
            "Capital": "New Delhi"
        },
        {
            "Country": "US",
            "Capital": "Washington DC"
        }
    ]'
    $json | convertfrom-json
    
    Country Capital
    ------- -------
    India   New Delhi
    US      Washington DC

    Another way is to iteratively add rows to an empty array, then ConvertTo-Json.

    $page = @()
    
    $info = '' | select Country,Capital
    $info.Country = 'India'; $info.Capital = 'New Delhi';
    $page += $info
    
    $info = '' | select Country,Capital
    $info.Country = 'US'; $info.Capital = 'Washington DC';
    $page += $info
    
    $json = $page | convertto-json
    $json | convertfrom-json
    Country Capital
    ------- -------
    India   New Delhi
    US      Washington DC
    
  • Read a JSON file

    PS> cat .\test.json -Raw | convertfrom-json
    
    DeviceID Total Memory (GB) Free Memory (GB)
    -------- ----------------- ----------------
    A:       250               250
    B:       681               124
    C:       118               39
  • ConvertTo-Json (eg. from CSV)

    import-csv .\test.csv
    
    DeviceID Total Memory (GB) Free Memory (GB)
    -------- ----------------- ----------------
    A:       250               250
    B:       681               124
    C:       118               39
    
    
    import-csv .\test.csv | convertto-json
    
    [
        {
            "DeviceID":  "A:",
            "Total Memory (GB)":  "250",
            "Free Memory (GB)":  "250"
        },
        {
            "DeviceID":  "B:",
            "Total Memory (GB)":  "681",
            "Free Memory (GB)":  "124"
        },
        {
            "DeviceID":  "C:",
            "Total Memory (GB)":  "118",
            "Free Memory (GB)":  "39"
        }
    ]

    Use the -Compress switch to strip all extra spaces (and minify the JSON file).

  • ConvertFrom-Json
    When we ConvertFrom-Json, we get a System.Object[] array. It doesn't actually create objects of the expected data type.
    Using ForEach, operate on each entry & format it in the desired way.

    cat .\test.json | convertfrom-json | gm
    
    TypeName: System.Object[]
    ...
    
    
    cat .\test.json | convertfrom-json | forEach {
        $_ | select Name,Size,@{name='TS'; expression={$_.timestamp -as [timespan]}}
    }
  • Modify JSON content
    To modify is to create a new file. To modify, access each entry using forEach, modify it, then save as new file.

    • Your JSON file has student marks for an exam. Initially, you had filled it with test entries, but now, marks have been appended even for these test entries. Default marks of test entries to Null, so it doesn't skew calculations for entire class.
    $json = cat test.json | convertfrom-json
    $json.update | forEach {
        if ($_.Name -eq 'test'){
            $_.Marks = $Null
        }
    } |
    convertto-json -depth |
    • Modify JSON: Example where good forEach logic is used.
  • Use a simple REST API & operate on its headers to make a table.

Databases

SQLite

http://ramblingcookiemonster.github.io/SQLite-and-PowerShell/ https://www.darkartistry.com/2019/08/create-insert-and-query-sqlite-with-powershell/ https://www.tutorialspoint.com/sqlite/index.htm

PDF

See: [1], [2], iText (examples), iText (videos)

iText offers a .NET API to deal with PDFs (C#, PS).
The latest version is iText7, but due to a lack of proper documentation, we'll use iText5.

  • Export pdf from text file [BROKEN]
function Export-Pdf {
<#
.SYNOPSIS
Exports PDF from a text file.
.EXAMPLE
PS> Export-Pdf -FilePath test.txt

PS> Export-Pdf -FilePath test.ps1
#>
    [cmdletBinding()]
    param(
        [string]$FilePath
    )
    # Invoke contents of binary
    $DLLPath = 'C:\Program Files\myBinaries\itext5\itextsharp.dll'
    Add-Type -Path $DLLPath -ErrorAction Stop
    # Convert TXT -> PDF
    $OutFile = "$($FilePath.BaseName).pdf"
    $File = New-Object -TypeName iTextSharp.text.Document
    $FileStream = New-Object -TypeName IO.FileStream -ArgumentList ($OutFile, [System.IO.FileMode]::Create)
    $Paragraph = New-Object -TypeName iTextSharp.text.Paragraph
    [iTextSharp.text.pdf.PdfWriter]::GetInstance($File, $FileStream)
    [iTextSharp.text.FontFactory]::RegisterDirectories()
    $Paragraph.add((Get-Content -Path $($FilePath.FullName) |
        forEach {"$_ `n"}
    ))
    $File.open(); $File.add($Paragraph); $File.close();
}

PDFtk is another useful shell utility.

# Download PDF (Python3.8.6 documentation)
iwr 'https://docs.python.org/3/archives/python-3.8.6-docs-pdf-a4.zip' -OutFile python.zip
# Extract content (use either 7zip or .NET module)
7z e .\python.zip -r
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::ExtractToDirectory("$(pwd)\python.zip", "$(pwd)")
# Retain only 'reference.pdf'
ls *.pdf | where {$_.BaseName -notmatch '^ref'} | del
ren .\reference.pdf .\test.pdf;

# Get number-of-pages in file
(pdftk test.pdf dump_data) -match 'NumberOfPages'
# Encrypt file
pdftk test.pdf output test_encrypted.pdf user_pw password allow printing
# Decrypt file
pdftk test_encrypted.pdf input_pw password output test.pdf
# Merge files
pdftk .\A.pdf .\B.pdf cat output mix.pdf
pdftk *.pdf cat output mix.pdf
# Split files
pdftk test.pdf cat 1-10 21-30 31-40 output mix.pdf
# Alternate & Merge files ('odd.pdf' is in ascending/descending order)
pdftk A=even.pdf B=odd.pdf shuffle A B output mix.pdf
pdftk A=even.pdf B=odd.pdf shuffle A Bend-1 output mix.pdf
# Split entire file into individual page-files
pdftk test.pdf burst

Compressed Files

Refer: System.IO.Compression.ZipFile

  • List contents of a compressed file

    Add-Type -AssemblyName System.IO.Compression.FileSystem
    $zip = [System.IO.Compression.ZipFile]::Open('test.zip',2)
    $zip.Entries.fullName
    ...
    $zip.Dispose()
  • Compress a file/folder

    $Source = '..\test\'; $Destination = 'test.zip';
    Add-Type -AssemblyName System.IO.Compression.FileSystem
    [System.IO.Compression.ZipFile]::CreateFromDirectory($Source, $Destination)
  • De-Compress a file

    $Source = 'test.zip'; $Destination = '..\test\';
    Add-Type -AssemblyName System.IO.Compression.FileSystem
    [System.IO.Compression.ZipFile]::ExtractToDirectory($Source, $Destination)
  • Add files to an existing compressed file
    Open the file as a ZipFile object using Open(). Here, 0,1,2 = Read, Write, R/W denotes the R/W access.
    Use CreatEntryFromFile() method to add entries. Here, 0,1,2 = Fast, Best, NoCompression denotes the compression mode.

    $Path = 'test.zip'; $file1 = 'test.pdf'; $file2 = 'test.mp4'
    Add-Type -AssemblyName System.IO.Compression.FileSystem
    $zip = [System.IO.Compression.ZipFile]::Open($Path,2)
    [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($zip, $file1, 'test.pdf', 0)
    [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($zip, $file2, 'test.mp4', 1)
    ...
    $zip.Dispose()
  • Compress/De-Compress specific files

    This compresses .txt files, and adds them to a ZIP archive.

    Add-Type -AssemblyName System.IO.Compression.FileSystem
    $zip = [System.IO.Compression.ZipFile]::Open('test.zip',2)
    
    ls *.txt | forEach {
        [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($zip, $_.FullName, $_.Name, 0)
    }
    ...
    $zip.Dispose()

    This de-compresses .txt files & files modified in the last month.

    Add-Type -AssemblyName System.IO.Compression.FileSystem
    $zip = [System.IO.Compression.ZipFile]::Open('test.zip',2)
    $zip.Entries | where Name -like *.txt | forEach {
        [System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, $_.Name)
    }
    $zip.Entries | where {$_.LastWriteTime.DateTime -gt (Get-Date).AddMonths(-1)} | forEach {
        [System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, $_.Name)
    }
    ...
    $zip.Dispose()

Text Editors

GNU Nano

See: .nanorc

Installation

  • Download latest binary from here.
  • Extract all *.nanorc files to C:/Users/USERNAME/nanorc/, and .nanorc to C:/Users/USERNAME/.
  • After opening nano .nanorc, uncomment necessary lines & insert include "C:/Users/USERNAME/nanorc/*.nanorc" for syntax-highlighting.

nanorc Configuration

Custom Behaviour

Option Description
set linenumbers display line-numbers
set mouse enable mouse drag/click support
set nohelp don't display help lines at bottom of screen
set regexp enable regex search by default
set rebinddelete enable backspace/delete keys to work correctly
set rebindkeypad enable numeric keypad
set zap delete entire selection using backspace/delete
set smooth enable smooth scrolling
set tabsize 4 set tab-size to 4 spaces
set tabstospaces convert tabs to spaces when saving file
set tempfile auto-save file on exit
set noconvert don't convert from dos/mac to unix format

Custom Key-Bindings

# Help, Find/Replace, Comment/Uncomment
bind F1 help all
bind M-F whereis all
bind M-W findnext all
bind M-Q findprevious all
bind M-R replace all
bind ^/ comment all
# Copy/Cut/Paste
bind M-C copy main
bind M-X cut main
bind M-V paste main
bind M-Z undo main
bind M-Y redo main
#bind ^H chopwordleft main
#bind ^H chopwordright main

Key-Bindings

Key-Binding Description
ESC+ESC+<0-255> Prints ASCII character corresponding to 0-255
Alt+3 Comment/Uncomment
Alt+Shift+3 Display line numbers
Ctrl+K/U Cut/Paste
Alt+6 Copy current line
Alt+U/E Undo/Redo
Ctrl+C Cursor position (row, column, character index)
F2 Exit
Ctrl+S; F3 Save/Save-As
Ctrl+W,Alt+W Find/Find-Next
Alt+R Replace
  • select/delete all
    Use Alt+T to cut from current line to eof.
  • work with several files at once
    Open multiple files using nano test.ps1 test.py test.txt; go back-and-forth using Alt+, and Alt+..
  • insert text from files
    Use Ctrl+R to insert-from-file. Enter filename, then press enter.
  • save backup files
    Run nano -BC .\backup test.txt. B/C => backup/path-to-backup-directory.

Git

GitHub CLI

Configuration

  • Login
gh auth login
gh auth login --with-token $Token_GitHub
gh auth logout
gh auth status

Here $Token_Github is an environment variable storing the token string.

  • Setup usage access limit
gh auth refresh                                     # Sets min access scope
gh auth refresh --scopes write:org,read:public_key  # Sets additional scopes
  • Setup default editor
gh config set editor nano # GNUNano
gh config set editor subl # SublimeText

Gist

  • List all gists
gh gist list
$Gists=@(); gh gist list | forEach {$Gists += $_.ToString().Split('')[0]}
  • Create a new gist (multiple files: code, text, stdin)
gh gist create --public test.ps1 test.py test.txt - -d 'Gist: Test'
  • View files from a gist
gh gist view 90dd9b9ed4402a8ea82ec8048935ec59
gh gist view $Gists[0]
gh gist view $Gists[1] -f gistfile2.txt
  • Edit files from a gist
gh gist edit 90dd9b9ed4402a8ea82ec8048935ec59 -f gistfile2.txt
gh gist edit $Gists[1] -f gistfile2.txt

Repo

  • View repo (README)
gh repo view
gh repo view crtejaswi/quiz

Release

  • List all releases of a repo
gh release --repo opencv/opencv list
  • List all releases of a repo with full info
gh release --repo opencv/opencv view
  • Download a release for a repo
gh release --repo opencv/opencv download 4.5.0
gh release --repo opencv/opencv download --pattern '*docs.zip'

# Ensure that the complete file has been downloaded
(ls .\opencv-4.5.0-docs.zip).Length/1MB

Alias

You can set aliases for routine commands using:

gh alias set <alias> <expansion>
# USAGE: gh <alias>

gh alias set pv 'pr view'
gh pv -w 123

GitHub Account

Authentication using SSH Keys
See: [1], [2].

Ensure that OpenSSH is enabled on Windows, then:

cd ~; ssh-keygen
Get-Service ssh-agent | Set-Service -StartupType Manual
ssh-agent -s
ssh-add ~/.ssh/id_rsa
cat ~/.ssh/id_rsa | scb

Paste this SSH key to your Github account.
Check SSH key status using ssh -T git@github.com, then move to project repo and:

# Check connection protocol (HTTPS/SSH)
git remote -v
# HTTPS -> SSH
git remote set-url origin git@<GIT-PROVIDER>:<USERNAME>/<REPO>.git
#git remote set-url origin git@github.com:CRTejaswi/resources.git
# Check connection protocol (HTTPS/SSH)
git remote -v

Parameters

Common Parameters
Common parameters are universal cmdlet parameters.

-Debug (-db)
-ErrorAction (-ea)
-ErrorVariable (-ev)
-InformationAction (-infa)
-InformationVariable (-iv)
-OutBuffer (-ob)
-OutVariable (-ov)
-PipelineVariable (-pv)
-Verbose (-vb)
-WarningAction (-wa)
-WarningVariable (-wv)

Parameter Names
For consistent names of parameters, PS lists 7 categories of parameter names:

Category Parameters
Activity Append, Insert, Include, Create, Erase, Force, Filter, Log, Recurse,
Date & Time Created, Accessed, Modified, Before, After
Formatting As, Encoding, Width, Wrap
Property Count, Id, Name, Value, Property, Regex
Quantity All, Allocation, Count, Scope
Resource Attribute, Drive, Port, Interface, Domain, Job, LiteralPath, Path, URL, Type
Security Credential, Privilege

Passing Parameters to a script
Arguments to a script/cmdlet can be passed in 3 equivalent ways:

Get-ChildItem -Path $env:windir -Filter *.dll -Recurse

Get-ChildItem `
    -Path $env:windir `
    -Filter *.dll `
    -Recurse

$myargs = @{
    Path = "$env:windir"
    filter = '*.dll'
    Recurse = $true
}
Get-ChildItem @myargs

Specifying Parameters in a Script

  • Passing args by index
    You can access the Shell arguments in the called script using $args array.
  • Passing args by name
    You can access the Shell arguments in the called script using param(...) statement.

By specifying CmdletBinding (at the top of script), we can add several features to our parameters.

  • Enabling the features
    All of these features listed below need CmdletBinding to be enabled.

    <#
    # Comments
    #>
    [CmdletBinding()]
    
  • Mandatory parameters
    Here, we make $myVar a mandatory parameter. (The HelpMessage is optional).

    param (
        [Parameter (Mandatory=$True, HelpMessage='A variable-name is mandatory')]
        [string]$myVar,
    
        [int]$Var1,
        [int]$Var2,
    )
    
  • Parameter aliases
    Here, we give an alias (name) to $myVar.

    param (
        [Parameter (Mandatory=$True)]
        [Alias ('name')]
        [string]$myVar,
    
        [int]$Var1,
        [int]$Var2,
    )
    
  • Validating parameter input
    Here, we specify a set of valid inputs for $Var1 & $Var2.

    param (
        [string]$myVar,
    
        [ValidateSet(0,1)]
        [int]$Var1,
        [ValidateSet(0,1)]
        [int]$Var2,
    )
    

    Read more about validation tricks, here.

  • Using Write-Verbose to print progress information
    Using Write-Verbose (instead of Write-Host) in our scripts allows us to optionally determine the progress of our code.
    Verbose output is visible only when the script is run with -verbose switch. But, you cannot set its foreground/background colors (as you can with Write-Host), since PS has predefined color-codes for Verbose/Debug/Warning.

    Write-Verbose "Using $myVar Values"
    

Referencing Passed Parameters in a Script
A system hashtable, $PSBoundParameters contains all the parameters & their values passed to a script.
See Get-Login for the actual script.

function Get-myLogin{
    [cmdletBinding()]
    param(
        [string]$Site,
        [switch]$List,
        [switch]$Browser
    )

    $logins = import-csv $myLogins
    switch ($PSBoundParameters.keys){
        'Site' {$match = $logins | where site -eq $Site; $match.username,$match.password | scb}
        'List' {$logins.site}
        'Browser' {firefox $match.link}
    }
}

Default Parameters
Refer $PSDefaultParameterValues.
You can set default values for parameters such as -Path, -Credential, ... by adding these values into a built-in variable, $PSDefaultParameterValues.
Note that the scope of $PSDefaultParameterValues is limited to the shell when specified in PS, and to the script when specified in a .ps1 file. This trick can be handy to specify defaults for a script in the script itself and not affect the shell's defaults.

  • Specify default-credentials for every cmdlet that has a -Credential parameter.

    $credential = Get-Credential -UserName Administrator -Message "Enter Admin's Password"
    $PSDefaultParameterValues.add('*:Credential', $credential)
    
  • Ask for credentials everytime an invoke-command cmdlet is executed.

    $PSDefaultParameterValues.add('Invoke-Command:Credential', {Get-Credential -UserName Administrator -Message "Enter Admin's Password"})
    

    Since invoke-command runs on local/remote PCs, this works as a basic security measure.

Scripting

Review: Control Flow, Filtering & Comparisons.
Also See: myScripts

Basics

Operators
Refer about_operators.

Name Description Example
-as type casts an object 1000/3 -as [int] => 333
-is type checks an object 12.45 -is [int] => False
-replace replace substring within a string 'Hello World' -replace 'e',3 => H3llo World
-join convert array to delimited-string `1,2,3,4,5 -join '
-split convert delimited-string to array "1 2 3 4 5" -split " " => 1, 2, 3, 4, 5
-like wildcarded substring matching 'this' -like '*his*' => True
-contains checks if an object exists in a collection 'abc','bcd','cde' -contains 'bcd' => True
-in checks if an object exists in a collection 'bcd' -in 'abc','bcd','cde' => True

Manipulating Strings & Dates

  • Pipe any string to get-member to list all associated properties/methods.
    'Test' | gm
    
  • Pipe get-date to list all associated properties/methods.
    get-date | gm
    
    When working with WMI objects, the dates may not be straight-forward.
    gwmi win32_operatingsystem | select lastbootuptime
    
    lastbootuptime
    --------------
    20200602222520.500000+330
    
    For this, use ConvertFromDateTime() & ConvertToDateTime() methods. (Check using gwmi win32_operatingsystem | gm)
    $os = gwmi win32_operatingsystem
    $os.ConvertToDateTime($os.LastBootUpTime)
    
    Tuesday, June 2, 2020 10:25:20 PM
    

Script Blocks ({})
Refer about_script_blocks.
Anything within {} is a script block.

https://www.youtube.com/watch?v=u6tdj1IFbpw https://www.sconstantinou.com/powershell-script-blocks/ https://www.youtube.com/watch?v=WP_Olf8GH_g https://www.youtube.com/watch?v=uoH6mnzwSZc

Clipboard

See: Clipboard, Get/Set Clipboard, xclip

Operations (gcb/scb)

  • Copy filepaths
scb; scb (ls B:\CRTejaswi\Codes).FullName -Append
  • Copy documentation
scb; scb (man Set-Clipboard -Append) -Append
  • Copy contents of a file
scb; scb (cat $Notes)
  • Cmdlets to copy clipboard to file [1]
Start-ClipboardCopy -FilePath $Clipboard
Stop-ClipboardCopy
  • Print/Create PDF from copied text

    gcb | out-printer
    
  • Copy a filepath(s) to clipboard (So it becomes easy to paste in GUI)

    ls *.pdf | % {$_.fullname} | scb
    (ls file.pdf).fullname | scb
    
  • Convert text (from docx) to epub/html/pdf

    start input.docx
    gcb | pandoc $_ --metadata title='Title' +RTS -Ksize -RTS -o output.epub
    gcb | pandoc $_ --metadata title='Title' -o output.html
    gcb | pandoc $_ --metadata title='Title' --pdf-engine=xelatex -o output.pdf
    
  • Generate epub/mobi from text files.

    (ls) -match $Pattern | forEach {pandoc $($_.FullName) --metadata title="$($_.BaseName)" --epub-cover-image="$($_.BaseName).jpg" -o "$($_.BaseName).epub"}
    ls *.epub | forEach {kindlegen $_.FullName}
    
  • Copy code(s)/text & save as code-file/text-file
    ascii is ideal. utf8 gives UTF-8 with BOM, so, you'll have to re-encode in editor.

    gcb | out-file -Encoding ascii output.py
    gcb | out-file -Encoding utf8 output.py
    

    Instead of creating a new file each time, simply append to the original:

    gcb | out-file -Append -Encoding ascii output.md
    

    This is ideal for simultaneous Copy & Paste (if you are too lazy to use Ctrl+C,Ctrl+V repeatedly).

  • Open links in Firefox

    # Copied
    firefox (gcb)
    # From text-file
    firefox (cat .\input.txt)
    

    A better way is to create a PS User-Profile

    notepad $profile
    -> Add variables/methods... and save.
    -> Run them from Shell
    

    To $profile, append path of file containing links $mylinks=B:\CRTejaswi\Documents\Links.txt.
    Open links using:

    firefox (cat $mylinks)
    

    Use subl $mylinks to update links you want to visit now.
    Read more about PS User-Profiles using: man about_profiles -ShowWindow

Linux

See: WSL, Tutorial, Blog

Setup

# Enable WSL
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
# Enable VM
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
# Download/Install WSL2 Kernel Package (x86-64)
iwr https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi -OutFile wsl.msi
wsl.msi
# Set WSL2 as default
wsl --set-default-version 2
# Install Linux Distribution (Debian)
firefox https://www.microsoft.com/en-in/p/debian/9msvkqc78pk6
# Check WSL Version
wsl -l -v

Configuring Linux OSes

sudo apt-get clean
sudo apt-get install -f
sudo dpkg --configure -a
sudo apt-get update && sudo apt-get dist-upgrade

Installing Linux Binaries
Ensure that you have configured your OS. Then, refer the installation steps for your binary.
eg. For telegram-cli, ensure OS setup, then:

sudo apt-get install libreadline-dev libconfig-dev libssl-dev lua5.2 liblua5.2-dev libevent-dev libjansson-dev libpython-dev make

Install Snap to install telegram as indicated here.

NET

See: Microsoft Reference, Microsoft Videos, C# In A Nutshell.

  • Eject USB drive

    $driveEject = New-Object -comObject Shell.Application
    $driveEject.Namespace(17).ParseName("D:").InvokeVerb("Eject")
    
  • Write templated strings to a file
    This writes file i.mp4; (i = 1-20) to MERGE.txt, and then, attempts to merge these video files.

    $page = @(); $n=1; while ($n -ne 21) {$page += "file $n.mp4"; $n++}
    $page | out-file -encoding ascii MERGE.txt
    ffmpeg -f concat -safe 0 -i MERGE.txt -c copy OUTPUT.mp4
    

    We create an empty array $page, append entries to it, and pipe it to MERGE.txt.

  • Print strings following a matching sub-string
    From CONFIG, I want to query the cmd to merge files using FFmpeg.
    Logic: -> print 10 lines after a 'merge' (inclusive) is encountered.

    These cmd gets ffmpeg lines out of CONFIG.

    (cat $myconfig) -match 'ffmpeg'
    

    https://devtipscurator.wordpress.com/2016/11/25/how-to-filter-contents-in-a-text-file-using-powershell/

Temporary Files

Create/Delete A Temporary File

$tmp = New-TemporaryFile
...
del $tmp.FullName -Force
  • Append source-code & outputs of code-files
    Assuming you have a temporary file $tmp, and source file names test;
    py .\test.py | out-file $tmp.fullname; cat .\test.py,$tmp.fullname | out-file -encoding ascii Py3.md -append
    
    .\test.exe | out-file $tmp.fullname; cat .\test.c,$tmp.fullname | out-file -encoding ascii C.md -append
    

HTTP Requests

PS gives Invoke-WebRequest (aka curl) to work with webpages.

NOTE:
Before doing anything useful, make sure to install & configure Internet Explorer. (Although you can make do without this by using -UseBasicParsing switch; this is an absolutely fuckall workaround. This is because using it doesn't give you access to the webpage's DOM, which means, you cannot parse it, or get anything useful out of it. It's like standing with a gun - a water gun. So, just install the damn thing!)

  • Install from here.

  • To configure: Win+I >> Apps & Features >> Optional Features >> Add a feature >> Internet Explorer 11. Then, Win+R >> Control >> Turn Windows Features ON/OFF >> [x] Internet Explorer. Restart PC.

  • Open up Internet Explorer, set to Recommended Settings. Done!

  • Get contents of a URL
    This generates a local copy of my CV.

    $response = Invoke-WebRequest -Uri crtejaswi.github.io/CV
    $response.Content | out-file -encoding utf8 test.html
    
  • Get all links from a webpage
    This gets all links from my CV.

    $links = (curl crtejaswi.github.io/CV).links.href
    
  • Get weather (temperatures)
    Retrieving data for Delhi (28.7041° N, 77.1025° E).

    $URI = 'https://weather.com/en-IN/weather/today/l/28.7041,77.1025?temp=c'
    $response = curl -Uri $URI
    ($response.allelements | where { $_.class -Match "\w+--location--\w+" -or $_.class -Match "\w+--tempHiLoValue--\w+" }).innerText
    
    Rohini Sector 2, Delhi Weather
    35°/26°
  • Get HTML (WebRequest) & Get JSON (RestMethod).

    $response = Invoke-WebRequest -Uri 'https://github.com/CRTejaswi/API/blob/master/CV.json'
    $response.content | out-file -encoding ascii test.html; firefox test.html
    
    $response = Invoke-RestMethod -Uri 'https://raw.githubusercontent.com/CRTejaswi/API/master/CV.json'
    $response | convertto-json | out-file -encoding ascii test.json; firefox test.json
  • Get all of my Youtube playlists.

    $response = curl -Uri 'https://www.youtube.com/channel/UC5NM5NZrw1hV6hgxnaPQPNw/playlists'
    $links = $response.links
    $links.href -match '^/playlist'
    
    /playlist?list=PL3pGy4HtqwD10u_vC6AksMg_RrIPMzvIA
    /playlist?list=PL3pGy4HtqwD3gyn8LDKSjCVDCbeyhbMTt
    ...

    Assuming absolute links; this opens all of them in a browser:

    $links.href -match '^/playlist' | out-file -encoding ascii $tmp.fullname
    firefox (cat $tmp.fullname)

    This won't work, since all links are relative. This works:

    $page = @()
    $page += ($links.href -match '^/playlist')
    $page = $page.replace('/playlist','www.youtube.com/playlist')
    $page | out-file -encoding ascii $tmp.fullname
    firefox (cat $tmp.fullname)
  • Append to my Youtube playlist.
    Given a playlist; append it to my own playlist, so I can easily watch it on my phone/pc/tv.
    Since, we'll send a POST request, we need to use YouTube's API.
    To manually insert videos from a playlist, we add &disable_polymer=true to the original link, then Add All. Is this any useful for querying the API?

REST APIs

MS Office

Windows offers assembly-level (.dll) .NET API to access MS Office (Core), MS Word, MS Excel, and a few other Office utilities.
Instances can be accessed using COM objects in .NET/PS.
These can also be accessed without having MS Office installed on your PC by making use of PS modules available through PSGallery.

Word

See: Word.Application, Documents
[Needs MSOffice installed on your PC.]

$FilePath = 'test.docx'
$MSWord = New-Object -ComObject Word.Application
$MSWord.visible = $True
$file = $MSWord.Documents.Add()
$content = $MSWord.selection

$content.typeParagraph()
$content.typeText('This is a test document.')
$content.typeText("`nContent added by $($env:USERNAME) on $(Get-Date -Format 'dd-MM-yyyy hh:mm')")
$content.font.bold = 1; $content.typeText("`nBold Text"); $content.font.bold = 0;
$content.font.italic = 1; $content.typeText("`nItalic Text"); $content.font.italic = 0;
$content.font.underline = 1; $content.typeText("`nUnderlined Text"); $content.font.underline = 0;

$file.saveAs($FilePath, 16); $file.close();
$MSWord.quit(); $null = [System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$MSWord);
Remove-Variable MSWord
  • I/O from text files
$content.typeParagraph()
$content.font.bold = 1; $content.typeText("`nContent added by $($env:USERNAME) on $(Get-Date -Format 'dd-MM-yyyy hh:mm')`n"); $content.font.bold = 0;
$content.typeText("$(cat test.md)")

If you do not have MS Office installed, you can use the PSWriteWord module (Install-Module PSWriteWord) to deal with docx files.
See: [1] [2] [3]

Excel

See: Excel.Application, Worksheets
[Needs MSOffice installed on your PC.]

$FilePath = 'test.xlsx'
$MSExcel = New-Object -ComObject Excel.Application
$MSExcel.visible = $True
$file = $MSExcel.Workbooks.Add()
$worksheet = $file.Worksheets.Item(1)

$worksheet.cells.item(1,1) = "ProcessName"
$worksheet.cells.item(1,2) = "Id"
$worksheet.cells.item(1,3) = "Handles"

$row = 2; $column = 1;

gps | select processName,id,handles | forEach {
    $worksheet.cells.item($row,1) = $_.processName
    $worksheet.cells.item($row,2) = $_.id
    $worksheet.cells.item($row,3) = $_.handles
    $worksheet.columns.autofit()
    $row++
}

$file.saveAs($FilePath); $file.close();
$MSExcel.quit(); $null = [System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$MSExcel);
Remove-Variable MSExcel

If you do not have MS Office installed, you can use the ImportExcel module (Install-Module ImportExcel) to deal with xlsx files.
See: [1] [2] [3] [4]

gps | Export-Excel test.xlsx -Show

Android SDK

See: Android SDK, Setup.

To-Do

Also, finish review exercises & put code in relevant sections.

PowerShell-Python

Execute Py3 scripts from PS, loading/storing data directly from the latter.

https://www.apress.com/gp/book/9781484245033 https://github.com/Apress/powershell-and-python-together

PowerShell-JavaScript

Unsorted

scb (ls .\test.txt).FullName
scb (ls $Directory -File).FullName
scb (ls (ls $NotesPS).Directory -File).FullName
  • Copy file-content
scb (cat .\test.txt)
  • Open files from the same directory as a file whose path is known
nano "$((ls $NotesPS).Directory)/scripts.md"
  • Edit multiple files
nano test.txt test.py test.ps1
subl test.txt test.py test.ps1
  • Rename files
    The $_.extension is helpful when dealing with similar files but different encodings (eg. BMP, JPG, GIF, PNG, SVG).
$i=1; ls | forEach {ren $_.name "$i$($_.extension)"; $i++}
  • Stop process
gps | where {$_.Name -match 'vlc'} | kill
  • Play music as system-tray app
vlc --qt-start-minimized --play-and-exit $Music
  • Change directory to that of a file whose path is known
pushd (ls $NotesPS).Directory
popd
  • Save/Open Bookmarks
- Structure/Save bookmarks to folder.
  eg. Lookup Youtube for videos on "Reddit API".
- Copy bookmarks folder, save to file, delete folder.
- Open files in browser
  firefox (cat $myLinks -Last 10)
  • Custom Web-Search cmdlets
Search-Web

$Name = $Name.ToLower().Replace(' ','+')
$Option = 0

$Google        = "https://www.google.com/search?q=$Name&tbm=$Option" # Option: isch, vid, bks, shop, fin
$Youtube       = "https://www.youtube.com/results?search_query=$Name"
$Twitter       = "https://twitter.com/search?q=$Name"
$Wikipedia     = "https://en.wikipedia.org/wiki/Special:Search?search=$Name"
$StackOverflow = "https://stackoverflow.com/search?q=$Name"
$Reddit        = "https://www.reddit.com/search/?q=$Name"

Search-Specs

$Phone  = "https://www.gsmarena.com/res.php3?sSearch=$Name"
$Laptop = "https://laptopmedia.com/specs/?q=$Name"
  • FFMpeg: remove audio
ffmpeg -i 720p.mp4 -vcodec copy -an 720p-nosound.mp4
  • FFMpeg: resize video
ffmpeg -y -i .\INPUT.mp4 -vf scale=-2:720,setsar=1:1 -c:v libx264 -c:a copy 720p.mp4
ffmpeg -y -i .\INPUT.mp4 -vf scale=-2:480,setsar=1:1 -c:v libx264 -c:a copy 480p.mp4
  • FFMpeg: merge audio/video tracks
    Here, the chosen audio was 1:00 min, while the video was 1:30 min. So, the audio was looped twice and the output was clipped according to the video's duration (shortest duration).
    Ideally, you should use -stream_loop -1 (infinite loop) for audio & clip for video duration (so you don't need to know durations of individual audio/video), but this option is buggy right now.
    If the -stream_loop -1 works, it'll be helpful for bulk merges.
# Clip AV to one which is the shortest
ffmpeg -i 720p-nosound.mp4 -i INPUT.mp3 -shortest -c copy 720p.mp4
# Loop audio twice, and clip at the shortest
ffmpeg -i .\720p-nosound.mp4 -stream_loop 2 -i INPUT.mp3 -shortest -c copy 720p.mp4

Create Youtube-ready video

- Remove audio from video
- Download Youtube audio
- Merge AV

ffmpeg -i 720p.mp4 -vcodec copy -an 720p-nosound.mp4
youtube-dl --no-cache-dir --extract-audio --audio-format mp3 -o "INPUT.%(ext)s" <URL>
ffmpeg -i 480p-nosound.mp4 -i INPUT.mp3 -shortest -c copy 480p.mp4
  • Concatenate/Copy strings to clipboard
scb (-join('$README = ',(ls README.md).FullName))
# $README = B:\CRTejaswi\Codes\MarkDown\Daily\README.md
scb (-join('$ContestCoding = ',(pwd).Path))
# $ContestCoding = B:\CRTejaswi\Codes\contest-coding
  • Rename selected files
ls | where {$_.BaseName -match $REGEX} | forEach {ren $_ "$($_.Basename.replace($CHAR,''))$($_.Extension)"}
  • Clear History
vim (Get-PSReadlineOption).HistorySavePath
  • List all current environment variables The env: drive contains this.
ls env: