powershell_notes - jonathanmorgan/shell_scripts GitHub Wiki
-
check powershell version: https://adamtheautomator.com/powershell-version/
(Get-Host).Version
-
find folder location of currently running script:
$my_directory = $PSScriptRoot
-
more options:
- https://stackoverflow.com/questions/40475853/how-to-find-location-path-of-current-script-in-powershell
- https://stackoverflow.com/questions/5466329/whats-the-best-way-to-determine-the-location-of-the-current-powershell-script
- https://www.tutorialspoint.com/how-to-get-the-path-of-the-currently-executing-script-in-powershell
-
- https://www.varonis.com/blog/windows-powershell-tutorials/
- https://www.powershelladmin.com/wiki/Main_Page
- bash-to-powershell: https://mathieubuisson.github.io/powershell-linux-bash/
- use VS Code with "PowerShell" extension.
- PowerShell ISE is old, no longer updated (https://www.varonis.com/blog/windows-powershell-tutorials/)
-
using
Invoke-Command
:-
Invoke-Command
documentation: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/invoke-command?view=powershell-7.1 -
remotely:
- https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_remote?view=powershell-7.1
- https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_remote_jobs?view=powershell-7.1
- https://4sysops.com/archives/use-powershell-invoke-command-to-run-scripts-on-remote-computers/
-
-
in the background, in jobs (multi-threaded, not multi-process):
- https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_jobs?view=powershell-7.1&viewFallbackFrom=powershell-6
- https://stackoverflow.com/questions/185575/powershell-equivalent-of-bash-ampersand-for-forking-running-background-proce
-
Start-Job
: https://www.tutorialspoint.com/how-to-run-powershell-commands-in-background - for separate process, see "Processes --> Start-Process" below, on starting a separate PowerShell process and passing it a script to run, with parameters.
-
for working on remote computers, can help in some of the same ways that
screen
ortmux
help on linux (persistent remote session, etc.). -
Create sessions:
$pss = New-PSSession -ComputerName <computer_name> -Credential <username>
-
will prompt for password
-
get existing sessions:
Get-PSSession
($pss = Get-PSSession
) -
enter remote session:
Enter-PSSession -Id 3
(enter ID of session from callingGet-Session
) -
then, you can treat this session like an SSH session or something like that. It will persist on the remote machine even if the remote desktop on that machine would otherwise time you out after some minutes of inactivity.
-
once done, close session using
Remove-PSSession
. Examples:Remove-PSSession -ComputerName <computer_name> -Id <ID> Remove-PSSession -Session $pss Remove-PSSession -Id 1
-
Notes:
- MS - about Remote Jobs: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_remote_jobs?view=powershell-7.2
- how to run remote jobs: https://www.howtogeek.com/117192/how-to-run-powershell-commands-on-remote-computers/
- overview: https://superuser.com/questions/643120/windows-powershell-screen-equivalent
- MS - about PSSessions: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_pssessions?view=powershell-7.2
- MS -
New-PSSession
cmdlet: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/new-pssession?view=powershell-7.2 - MS - Running Remote Commands: https://docs.microsoft.com/en-us/powershell/scripting/learn/remoting/running-remote-commands?view=powershell-7.2
- operators doc: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_operators?view=powershell-7.1
- assignment operators: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_assignment_operators?view=powershell-7.1
- https://ss64.com/ps/syntax-operators.html
-
in powershell, the back-tick ( "`" ) is equivalent to the unix back-slash:
-
it is used to escape characters in strings
-
you put it at the end of a line to make a command continue on the next.
-
- https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/capitalization-conventions
- https://poshcode.gitbook.io/powershell-practice-and-style/style-guide/code-layout-and-formatting
- https://poshcode.gitbook.io/powershell-practice-and-style/style-guide/naming-conventions
-
summary of different parameter options: https://theitbros.com/powershell-function-parameters/
-
param() structure
- basic overview: https://www.educba.com/powershell-function-parameters/
- good and detailed overview of
param()
structure: https://www.improvescripting.com/how-to-create-parameters-in-powershell/
-
Check if switch parameter is present: https://stackoverflow.com/questions/56809557/how-to-check-if-a-powershell-switch-parameter-is-absent-or-false
-
call
.IsPresent()
on the parameter variable. -
Example:
if ( $OutputDebug.IsPresent -eq $true ) { $my_debug_flag = $true }
-
-
help messages for each parameter:
- https://stackoverflow.com/questions/5237723/how-do-i-get-help-messages-to-appear-for-my-powershell-script-parameters
- https://duckduckgo.com/?t=ffab&q=powershell+params+output+help+text&ia=web
- comment-based help: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_comment_based_help?view=powershell-7.1
-
basics: https://jmmv.dev/2020/10/powershell-cmdlet-params.html
-
parameters must be first in script.
-
I can't get defaults to work inside param structure... so set defaults later.
-
types of parameters: https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/types-of-cmdlet-parameters?view=powershell-7.1
-
Example:
#==============================================================================# # parameters - must be at top of script (no code between top and "Param"). #==============================================================================# #[CmdletBinding()] Param( [Parameter(HelpMessage="Turn on debug messages.")] [switch]$DebugFlag, [Parameter(HelpMessage="Clean up after a test run.")] [switch] $CleanupFlag, [Parameter(HelpMessage="The path where you want output file stored.")] [string] $OutputFile ) # store parameters in expected variables $start_date_time_stamp = Get-Date -UFormat "%Y-%m-%d_%H-%M-%S" $debug_flag = $DebugFlag $cleanup_flag = $CleanupFlag if ( $OutputFile ) { $output_file_path = $OutputFile } else { $output_file_path = ".\${start_date_time_stamp}-logging_test.tsv" } Write-Host "params: debug_flag = ${debug_flag}; cleanup_flag = ${cleanup_flag}; output_file_path = ${output_file_path}"
-
Notes:
- parameters declared in the function declaration (not inside the function): https://www.red-gate.com/simple-talk/sysadmin/powershell/how-to-use-parameters-in-powershell/
- Advanced function parameters (includes details on inside function
param
): https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_advanced_parameters?view=powershell-7.1
-
Example:
function choose_config_value() { [CmdletBinding()] Param( [Parameter( HelpMessage = "Value for this config property passed on command line." )] $CommandLineValue, [Parameter( HelpMessage = "Value for this config property from config file." )] $ConfigValue, [Parameter( HelpMessage = "Default value for this config property." )] $DefaultValue, [Parameter( HelpMessage = "(optional) Convert to a particular type?" )] [string] $ConvertToType, [Parameter( HelpMessage = "Turn on debug messages." )] [switch] $OutputDebug ) # return reference $value_out = $null # declare variables $my_debug_flag = get_shared_debug #$my_debug_flag = $true $value_type = $null $value_type_name = $null if ( $OutputDebug.IsPresent -eq $true ) { $my_debug_flag = $true } # got a command line value? if ( ( $null -ne $CommandLineValue ) -and ( $CommandLineValue -ne "" ) ) { # got command line value - this overrides config file. $value_out = $CommandLineValue } elseif ( ( $null -ne $ConfigValue ) -and ( $ConfigValue -ne "" ) ) { # got config file value - use it. $value_out = $ConfigValue } else { # nothing set on command line or in config file - use default. $value_out = $DefaultValue } # are we converting? if ( $ConvertToType -eq $Script:CONVERT_TO_BOOLEAN ) { # check if string $value_type = $value_out.GetType() $value_type_name = $value_type.Name if ( $value_type_name -eq $Script:TYPE_NAME_STRING ) { # string - convert to boolean! $value_out = [System.Convert]::ToBoolean( $value_out ) } } elseif ( $ConvertToType -eq $Script:CONVERT_TO_INT ) { # cast to integer $value_out = [int]$value_out } # return it if ( $my_debug_flag -eq $true ) { Write-Host "Value: `"${value_out}`"" Write-Host "----> chosen from:" Write-Host "- CLI: `"${CommandLineValue}`"" Write-Host "- config: `"${ConfigValue}`"" Write-Host "- default: `"${DefaultValue}`"" Write-Host "- ( Convert to: ${ConvertToType} )" } return $value_out } #-- END function choose_config_value() --#
-
PowerShell string interpolation syntax
- variable reference syntax is just like bash: ${var_name}
- https://stackoverflow.com/questions/60323111/powershell-string-interpolation-syntax
-
check for empty:
-
https://mcpmag.com/articles/2015/12/14/test-variables-in-powershell.aspx
-
https://thinkpowershell.com/test-powershell-variable-for-null-empty-string-and-white-space/
-
Example (Get-Variable):
if ( -not ( Get-Variable 'ok_to_process' -Scope 'Global' -ErrorAction 'Ignore') ) { $ok_to_process = $true }
-
variable types:
-
call the
GetType()
method on a variable to get the type of its contents. -
Example:
$date = Get-Date $date.GetType()
-
https://devblogs.microsoft.com/scripting/powertip-find-the-type-of-an-object/
-
-
converting between types (not just int and string): https://adamtheautomator.com/powershell-convert-string-to-int/
-
Variable Contexts/Scopes:
-
Global
-
Local
-
Script - this is variables declared in the body of a powershell script (global to the script, but not "Global").
-
In a function, to reference a variable declared outside the function's scope, add the scope to the beginning of the variable name, separated by a colon. Example:
# not OK to process function add_error( $message_IN ) { # declare variables # not OK to process $Script:ok_to_process = $false # store error message if ( $Script:error_message -ne $null ) { $Script:error_message = "$Script:error_message`n${message_in}" } else { $Script:error_message = "${message_in}" } # DEBUG if ( $Script:debug_flag -eq $true ) { # output message Write-Host "Error added ( ok_to_process = $Script:ok_to_process ): ${message_in}" Write-Host "error_message:`n${Script:error_message_OUT}" } } #-- END function add_error() --#
-
Notes:
-
-
-
$null:
-
Get-Member - retrieve members of a given object (methods, variables, etc.): https://adamtheautomator.com/powershell-convert-string-to-int/
$date | Get-Member
-
check if variable is numeric type: https://stackoverflow.com/questions/10928030/in-powershell-how-can-i-test-if-a-variable-holds-a-numeric-value
-
Code:
function isNumeric( $value_in ) { # return reference $is_numeric_out = $false $is_numeric_out = $value_in -is [byte] -or $value_in -is [int16] -or $value_in -is [int32] -or $value_in -is [int64] ` -or $value_in -is [sbyte] -or $value_in -is [uint16] -or $value_in -is [uint32] -or $value_in -is [uint64] ` -or $_ -is [float] -or $_ -is [double] -or $_ -is [decimal] return $is_numeric_out } #-- END function isNumeric() --#
-
-
convert string to int::
-
Get-Date (like unix "time"): https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-date?view=powershell-7.1
-
Example with unix-style format string:
Get-Date -UFormat "%Y-%m-%d_%H-%M-%S" # 2021-09-22_10-41-12
-
Example with built-in ISO-8601 format:
Get-Date -Format "o" # 2017-08-15T12:10:34.4443084+03:00
- Examples: https://devblogs.microsoft.com/powershell/the-wonders-of-date-math-using-windows-powershell/
-
Use
New-TimeSpan
commandlet to calculate amounts of time between dates. -
Example:
$current = Get-Date $end= Get-Date $diff= New-TimeSpan -Start $current -End $end Write-Output "Time difference is: $diff"
-
Notes:
- join string array into string: https://morgantechspace.com/2021/01/how-to-join-string-array-into-one-string-in-powershell.html
-
special characters: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_special_characters?view=powershell-7.1
-
quoting rules: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules?view=powershell-7.1
- single-quotes are verbatim, double-quotes are interpreted.
-
remove quotes:
-
find
-
String.StartsWith()
,String.contains()
-
String.StartsWith Method (System) Microsoft Docs:
-
-
-
find and replace:
-
"-replace" flag is in here: https://social.technet.microsoft.com/Forums/office/en-US/f991c06a-c18d-48f8-a369-27735a66db05/how-to-remove-newline-carriage-returns-when-outputting-active-directory-username-properties
-
another good link with options: https://devblogs.microsoft.com/scripting/powertip-finding-letters-in-strings-with-powershell/
-
example:
function clean_string( $work_string_in ) { # return reference $cleaned_string_out = $null # remove tabs and carriage returns. $cleaned_string_out = $work_string_in -replace "`t|`r", "" # replace newlines with " ==> " $cleaned_string_out = $cleaned_string_out -replace "`n", "${Script:newline_replacement}" return $cleaned_string_out } #-- END function clean_string() --#
-
-
parsing strings with delimiters - Split() method:
-
https://www.mssqltips.com/sqlservertip/5427/parsing-strings-from-delimiters-in-powershell/
-
use the ".Split( "" ) function to turn a string with an item delimiter character or string into an array of the items.
- Example splitting on space:
$command_token_list = $command_in.Split( " " )
- Example splitting on space:
-
also include "Replace()" examples.
-
-
https://devblogs.microsoft.com/powershell/parsing-text-with-powershell-1-3/
-
-
split
parameter/command-
syntax is wonkier than just using the
Split()
method above. -
Examples:
- split on white space:
-split "red yellow blue green"
- split on a different character:
"Lastname:FirstName:Address" -split ":"
- split on white space:
-
storing result of commands in variables:
Split-Path
- Parse strings that are paths to get elements of path (file name, containing folder path, etc.).
-
Note: does not support
-LiteralPath
on some versions of Powershell 5.1 -
Example - get file name from path string:
$output_file_name = Split-Path -Path "${output_file_path}" -Leaf
-
Example - get containing folder path from path string:
$output_folder_path = Split-Path -Path "${output_file_path}"
-
$boolean_variable = [System.Convert]::ToBoolean($string_variable)
-
Notes:
-
create empty array:
@()
- Example:
$my_array = @()
- Example:
-
append item to array:
$my_array += $new_item
- note, this actually makes an entirely new copy of the array with the additional item on the end. Be advised.
-
count items in an array --> use ".count".
-
loop over items in an array:
$a = 0..9 foreach ($element in $a) { $element }
-
join string array into string: https://morgantechspace.com/2021/01/how-to-join-string-array-into-one-string-in-powershell.html
-
Notes:
- about arrays: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_arrays?view=powershell-7.1
- arrays deep dive: https://docs.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-arrays?view=powershell-7.1
- more info: https://www.varonis.com/blog/powershell-array/
-
create empty hash table:
@{}
- Example:
$my_hash_table = @{}
- Example:
-
add item to hash table:
$my_hash_table[ "key_name" ] = $value
-
count items in a hash table --> use ".count".
-
loop over items in a hash table:
$key_list = $my_hash_table.keys foreach ($key_name in $key_list ) { $current_value = $my_hash_table[ "key_name" ] Write-Host "- ${key_name} --> ${current_value}" }
-
Notes:
- about hash tables: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_hash_tables?view=powershell-7.1
- everything you need to know about hash tables: https://docs.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-hashtable?view=powershell-7.1
-
Equivalent of "touch"ing a file:
-
Note: for *-Item calls, you should always use
-LiteralPath
when it is available if you aren't using wildcards, to prevent accidental multiple matches.
- Get data from inside a file or object.:
Get-Content
- https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-content?view=powershell-7.1
Examples:
-
Read an entire file:
$config_json_string = Get-Content -Raw -LiteralPath "${config_file_path_in}"
-
Read an entire file that contains JSON, then convert it to an easy-to-uuse JSON object:
$config_json = Get-Content -Raw -LiteralPath "${config_file_path_in}" | ConvertFrom-Json
-
get last line (like linux
tail
!):Get-Content "${text_file_path}" -Tail 1
-
more info:
-
Retrieve item at a path. This could be a folder or file, or could be all items in a folder (add "
\*
" at the end). -
Returns objects for item that contain "Mode", "LastWriteTime", "Length", and "Name" by default.
-
Examples:
- get folder C:\Temp:
Get-Item C:\Temp
- get all items inside folder C:\Temp:
Get-Item C:\Temp\*
- get folder C:\Temp:
-
More info:
-
Retrieve all items within a particular item. Useful for retrieving contents of a directory, for example.
-
Examples:
- get items in folder C:\Temp:
Get-ChildItem -Path C:\Temp
- get items in folder C:\Temp:
- Get current working directory:
Get-Location
- https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-location?view=powershell-7.1
Allows you to change current working directory in side a script, similar to CD command.
- https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/set-location?view=powershell-7.1
- Example:
Set-Location "${folder_path}"
- Managing current locations; https://docs.microsoft.com/en-us/powershell/scripting/samples/managing-current-location?view=powershell-7.1
-
Test-Path
- check if file exists:-
Documentation:
-
Test-Path
example for file, no wildcards (-LiteralPath
, if wildcards, use-Path
):$does_file_exist = Test-Path -LiteralPath "${my_text_file_path}" -PathType Leaf
-
Test-Path
example for directory, no wildcards (-LiteralPath
, if wildcards, use-Path
):$does_file_exist = Test-Path -LiteralPath "${my_folder_path}" -PathType Container
-
Test-Path
example for either file or folder, no wildcards (-LiteralPath
, if wildcards, use-Path
):$does_folder_exist = Test-Path -LiteralPath "${my_folder_path}"
-
-
other options, including
Test-Path
: https://adamtheautomator.com/powershell-check-if-file-exists/
New-Item
- Create new item (either file, folder, or other types like registry entry).
-
Note: does not support
-LiteralPath
flag, just-Path
. -
Example (file):
New-Item -Path "${output_folder_path}" -Name "${output_file_name}" -ItemType "file"
-
Example (directory):
New-Item -Path "c:\" -Name "logfiles" -ItemType "directory"
Copy file - Copy-Item
-
Example (
-LiteralPath
= no substitutions, if wildcards, use-Path
):Copy-Item -LiteralPath "${folder_1_path}\${text_file_name}" -Destination "${folder_2_path}"
-
Example - recursive directory copy (
-LiteralPath
= no substitutions, if wildcards, use-Path
):Copy-Item -Recurse -LiteralPath "${folder_1_path}" -Destination "${folder_2_path}\${copied_folder_name}"
Move file - Move-Item
-
Example (
-LiteralPath
= no substitutions, if wildcards, use-Path
):Move-Item -LiteralPath "${xcopy_test_file_path}" -Destination "${xcopy_test_folder_path}"
Remove-Item
lets you delete files and folders (and other items).
-
Example (works for files and folders):
Remove-Item "${cleanup_folder_path}" -Recurse -Force
-
Example - remove everything inside a folder:
Remove-Item "${cleanup_folder_path}\*" -Recurse -Force
-
Notes:
- Use the
Invoke-Item
commandlet to open an item in its default handling program (for example, fortest.docx
, open it in Word). - https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/invoke-item?view=powershell-7.1
-
& $command_name $param_list
-
&
command accepts the string name of the command you want to run, and then a list of the params you want to pass to the command. It returns the output from execution of the command. -
this is more secure (less threat of command injection).
-
Example:
$test_command = "mkdir" $test_args = @( "test3" ) $test_output = & $test_command $test_args Write-Host "command output (test3): ${test_output}"
-
-
Invoke-Expression
- Just treats the string passed to it as a complete command, including parsing arguments by splitting on spaces.
- Example:
Invoke-Expression $command_text
- https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-expression?view=powershell-7.1
- code: https://stackoverflow.com/questions/3592851/executing-a-command-stored-in-a-variable-from-powershell
- https://stackoverflow.com/questions/50018274/why-does-invoke-operator-and-invoke-expression-produce-different-results-for
- PowerShell team saying to use
&
rather thanInvoke-Expression
: https://devblogs.microsoft.com/powershell/invoke-expression-considered-harmful/ - discussion: https://www.vistax64.com/threads/invoke-expression-vs.109699/
-
essentially, just assign the result of the command invocation to a variable (Powershell > bash here, I think).
-
Links:
- Process object: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process?view=net-5.0
- Get ID of currently running process: "
$PID
" - Get ID of a process you've started: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.id?view=net-5.0#System_Diagnostics_Process_Id
-
Start-Process
doc: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/start-process?view=powershell-7.1 -
Use
Start-Process
to start separate processes in a powershell script:Start-Process SAS -ArgumentList "-sysin ""C:\Temp\myProgram.sas""" -Wait
-
WHERE:
-
-Wait
- tells Start-Process to block until process completes. -
-ArgumentList
- lets you pass arguments directly to the process.
-
-
-
more info: https://adamtheautomator.com/start-process/
- includes link to
Invoke-Process
custom commandlet...
- includes link to
-
How to run a powershell script in a separate process:
- https://stackoverflow.com/questions/47722158/run-a-separate-powershell-script-asynchronously-while-continuing-the-current-scr
Start-Process PowerShell -Argument "C:\MyScript.ps1"
- for complex argument sets, make a list of the script path plus each token in the command (everything separated by spaces) and pass that as
-ArgumentList
(which could be a string that will get split on spaces, or could be an Array of the space-separated tokens).
-
calling processes on remote machines: https://social.technet.microsoft.com/Forums/Azure/en-US/7e5d19ba-e22c-43d9-883d-0286ab6cdef5/executing-powershell-script-with-parameters-remotely
-
notes:
-
Example:
if ( $hooha -eq $true ) { # do something } elseif ( $hooha -eq $false ) { # do nothing } else { # unexpected... }
-
from: https://adamtheautomator.com/powershell-for-loop/
- Many examples here.
-
syntax:
for (<Initial iterator value>; <Condition>; <Code to repeat>) { <Statement list> }
-
example with integer index:
# loop to create $output_file_count files in output folder. for ( $output_file_number = 1; $output_file_number -le $output_file_count; $output_file_number++ ) { # write random file to output folder $output_file_name = "${current_timestamp}-${output_file_number}" $output_file_path = write_random_file "${output_file_name}" "${output_file_size_bytes}" "${rd_output_folder_path}" Write-Host "--------> command output (file name = ${output_file_name}; file size = ${output_file_size_bytes}; output folder: ${rd_output_folder_path}):`n${output_file_path}" }
-
foreach
language construct: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_foreach?view=powershell-7.1-
example:
# foreach ($<item> in $<collection>){<statement list>} foreach ( $key in $hash_table.keys ) { $value = $hash_table[ "${key}" ] Write-Host "- ${key} --> ${value}" }
-
-
ForEach-Object
commandlet: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/foreach-object?view=powershell-7.1 -
basic syntax: https://teckangaroo.com/what-is-percentage-sign-in-powershell/
-
% notation:
-
example:
# while (<condition>){<statement list>} while ( $test_value -eq $true ) { Write-Host "looping... forever..." }
Start-Sleep
lets you pause execution of a powershell script for any number of minues and milliseconds, summed if both are present.
-
Notes:
-
Start-Sleep
doc - https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/start-sleep?view=powershell-7.1 -
Start-Sleep
usage, and other options: https://adamtheautomator.com/powershell-pause/
-
-
Examples:
# sleep for 5 seconds. Start-Sleep -Seconds 5
- https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions?view=powershell-7.1
- https://docs.microsoft.com/en-us/powershell/scripting/learn/ps101/09-functions?view=powershell-7.1
-
Note, this doesn't work like a standard method or function return - it does something in relation to streams, everything that happens in the function becomes part of the output or something, etc.
-
If you need a function to return something, especially something complicated like a hash table, list, or JSON object, store it in a Script variable and reference that variable after the call to retrieve the output.
- this could be either a direct variable reference, or a "get" function that just retrieves the variable contents and returns them (it works fine if it is the only thing in the function), for use in accessing the contents of the variable within a module.
-
More information:
- return documentation: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_return?view=powershell-7.1
- more details on how it works: https://theitbros.com/powershell-function-return/
- possible ways to get it to work (that did not work for me): https://evotec.xyz/powershell-returning-one-object-from-function-as-an-array/
- more on this issue of return returning everything a function does: https://www.reddit.com/r/PowerShell/comments/5zc7w1/help_returning_hashtables_from_functions/
- You can make classes in Powershell!: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_classes?view=powershell-7.1
Modules are files of powershell functions. They can also contain module variables, to help with processing.
Notes:
- modules overview: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_modules?view=powershell-7.1
- good resource on module variables: https://powershell.anovelidea.org/powershell/how-i-implement-module-variables/
- more detailed overview: https://docs.microsoft.com/en-us/powershell/scripting/learn/ps101/10-script-modules?view=powershell-7.1
- example module: https://dennisspan.com/powershell-function-library/
- old, includes module manifests: https://mikefrobbins.com/2013/07/04/how-to-create-powershell-script-modules-and-module-manifests/
- https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/import-module?view=powershell-7.1
- Example: TK tomorrow
-
SID is unique ID for user.
-
get SID of user:
-
https://www.optimizationcore.com/scripting/ways-get-current-logged-user-powershell/
-
https://stackoverflow.com/questions/2085744/how-do-i-get-the-current-username-in-windows-powershell
-
Example with WHOAMI command:
# get current user SID and username $user_info = WHOAMI /USER /FO CSV /NH $user_info_items = $user_info.Split( "," ) $user_username = $user_info_items[ 0 ].Replace( "`"", "" ) $user_sid = $user_info_items[ 1 ].Replace( "`"", "" ) Write-Host "running as SID: $user_sid; username: $user_username ( user info: $user_info )"
-
Example with [System.Security.Principal.WindowsIdentity]:
$user_info = [System.Security.Principal.WindowsIdentity]::GetCurrent() $user_username = $user_info.Name $user_sid = $user_info.User.Value Write-Host "running as SID: $user_sid; username: $user_username ( user info: $user_info )"
-
-
system name and FQDN:
# system information $system_name = hostname $system_dns_info = [System.Net.Dns]::GetHostByName( "${system_name}" ) $system_fqdn = $system_dns_info.HostName
-
Notes and examples:
-
-
system name from environment variable:
$env:computername
- from https://devblogs.microsoft.com/scripting/powertip-use-powershell-to-get-computer-name/
- beware - environment variable could be changed by user or other programs...
This is a mess on windows - nothing equivalent to top
to tie it all together and put a bow on it for you.
Notes:
- https://stackoverflow.com/questions/6298941/how-do-i-find-the-cpu-and-ram-usage-using-powershell
- https://www.powershellbros.com/run-script-to-check-cpu-and-memory-utilization/
The different methods to capture CPU usage seem to return different values at roughly the same point in time, and to not correlate with each other over time. So... perhaps just buy monitoring software?
Example:
$cpu_wmic_loadpercentage = wmic cpu get loadpercentage
$cpu_w32p_loadpercentage = (Get-CimInstance -Class Win32_Processor).LoadPercentage
$cpu_w32p_load_average = (Get-WmiObject -Class win32_processor -ErrorAction Stop | Measure-Object -Property LoadPercentage -Average | Select-Object Average).Average
if ( $include_cpu_average_in -eq $true )
{
$cpu_average = ( GET-COUNTER -Counter "\Processor(_Total)\% Processor Time" -SampleInterval 2 -MaxSamples 5 | Select-Object -ExpandProperty countersamples | Select-Object -ExpandProperty cookedvalue | Measure-Object -Average ).average
}
else
{
$cpu_average = "N/A"
}
$proc_count = (Get-Process).Count
$service_count = (Get-Service).Count
$running_service_count = (Get-Service | Where-Object Status -eq "Running").Count
Notes:
Example:
# get memory info. Uses `WIN32_OperatingSystem` class:
$comp_object = Get-WmiObject -Class WIN32_OperatingSystem
$mem_total = $comp_object.TotalVisibleMemorySize
$mem_free = $comp_object.FreePhysicalMemory
$mem_used = $mem_total - $mem_free
$swap_total = $comp_object.TotalVirtualMemorySize
$swap_free = $comp_object.FreeVirtualMemory
$swap_used = $swap_total - $swap_free
Notes:
-
This code is based on a more thorough function: https://docs.microsoft.com/en-us/archive/blogs/timid/getting-computer-memory-usage
-
Full function included below in case the archive ever goes away:
function Get-ComputerMemory { param ( [Parameter( Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [String[]]$ComputerName = @($env:COMPUTERNAME.ToLower()), [int]$digits = 3 ); process { foreach ($computer in $ComputerName ) { $object = 1 | Select-Object ComputerName, TotalPhysicalMemoryGB, FreePhysicalMemoryGB, UsedPhysicalMemory, TotalVirtualMemoryGB, FreeVirtualMemoryGB, UsedVirtualMemory; Write-Progress "Get-WmiObject Win32_ComputerSystem" "-ComputerName $computer" $wmiCompSys = Get-WmiObject Win32_ComputerSystem -ComputerName $computer; if (!$wmiCompSys) { Write-Warning "Unable to get Win32_ComputerSystem data from $computer"; } Write-Progress "Get-WmiObject Win32_OperatingSystem" "-ComputerName $computer" $wmiOpSys = Get-WmiObject Win32_OperatingSystem -ComputerName $computer; if (!$wmiOpSys) { Write-Warning "Unable to get Win32_OperatingSystem data from $computer"; } $object.ComputerName = $computer.ToLower(); # normalize to GB. NOTE: Win32_OperatingSystem returns KB while Win32_ComputerSystem returns bytes. $totalPhys = $wmiCompSys.TotalPhysicalMemory / 1GB; $freePhys = $wmiOpSys.FreePhysicalMemory / 1MB; $totalVirt = $wmiOpSys.TotalVirtualMemorySize / 1MB; $freeVirt = $wmiOpSys.FreeVirtualMemory / 1MB $object.TotalPhysicalMemoryGB = ("{0:N$digits}" -f ($totalPhys + .5)) -as [float]; $object.FreePhysicalMemoryGB = ("{0:N$digits}" -f ($freePhys + .5)) -as [float]; $object.UsedPhysicalMemory = [int]((100 * (1 - ($freePhys/$totalPhys))) + .5); $object.TotalVirtualMemoryGB = ("{0:N$digits}" -f ($totalVirt + .5)) -as [float]; $object.FreeVirtualMemoryGB = ("{0:N$digits}" -f ($freeVirt + .5)) -as [float]; $object.UsedVirtualMemory = [int]((100 * (1 - ($freeVirt/$totalVirt))) + .5); $object; } } }
-
Basic information:
- includes link to the thorough function above: https://community.spiceworks.com/topic/2203430-powershell-get-memory-use-or-get-free-memory
- calculate percent memory usage: https://shellgeek.com/powershell-get-memory-usage/
-
use
Write-Host
. -
Example code:
Write-Host "log line: ${test_output}"
-
Documentation: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/write-host?view=powershell-7.1
-
difference between write-output and write-host
- don't use
Write-Output
if you usereturn
in a function. - https://stackoverflow.com/questions/19754069/powershell-difference-between-write-host-and-write-output
- https://morgantechspace.com/2021/02/write-output-vs-write-host-in-powershell.html
- don't use
-
overview: https://devblogs.microsoft.com/powershell-community/how-to-send-output-to-a-file/
-
New-Item
- to create a new file (including before you can append anything to the file):New-Item
-
Example:
New-Item -Path "${output_folder_path}" -Name "${output_file_name}" -ItemType "file" -Value "${test_output}`n"
-
microsoft doc: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/new-item?view=powershell-7.1
-
-
Append to file:
-
Add-Content
-
example:
Add-Content -Path "${Script:output_file_path}" -Value "${command_log_line}"
-
note: fails if file is opened for read (by anti-virus, for example, or other file scanner). If you run into this problem, can use
Out-File -Append
- known issue: https://github.com/PowerShell/PowerShell/issues/5924
- fix merged into powershell in 2018 (sigh): https://github.com/PowerShell/PowerShell/pull/8091
-
Out-File -Append
-
example:
Out-File -FilePath "${Script:output_file_path}" -Append -InputObject $command_log_line -Encoding UTF8
-
more notes:
-
if output of string above is strange - space between each character in string.
- related to encoding?: https://adamtheautomator.com/out-file/
- adding
-Encoding UTF8
does indeed fix it... not sure why.
-
https://www.computerperformance.co.uk/powershell/file-outfile/
-
-
-
Replace existing contents with new information:
-
Set-Content
-
Out-File
-
-
Export-CSV
, for lists of powershell "objects", one list to a row:
-
https://www.vistax64.com/threads/is-there-an-equivalent-to-dev-null.75056/
-
https://jameswassinger.com/all-articles/using-powershell-redirection-for-script-logging
-
Examples:
.\script.ps1 *> script.log &{ Write-Warning "hello" Write-Error "hello" Write-Output "hi" } 3>&1 2>&1 > P:\Temp\redirection.log
-
-
ignoring output (like
> /dev/null
): https://stackoverflow.com/questions/5260125/whats-the-better-cleaner-way-to-ignore-output-in-powershell
-
Start-Transcript
: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.host/start-transcript?view=powershell-7.1 -
Stop-Transcript
: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.host/stop-transcript?view=powershell-7.1 -
using
Start-Transcript
andStop-Transcript
to capture output to file: -
during execution of script, and other options: https://stackoverflow.com/questions/1215260/how-to-redirect-the-output-of-a-powershell-to-a-file-during-its-execution
-
New-RandomData
commandlet - good function that does everything for you:-
code, and basic usage: https://www.powershelladmin.com/wiki/Create_cryptographically_secure_and_pseudorandom_data_with_PowerShell
-
source code:
-
Examples:
- 1 MB random file:
New-RandomData -Path "${file_folder_path}" -BaseName "${file_name_in}" -Size $file_byte_count -Extension ".txt"
- 128 byte random string:
$file_line = New-RandomData -Size 128 -StreamToSTDOUT
- 1 MB random file:
-
-
byte-by-byte using Get-Random (very slow):
-
Example:
[Byte[]] $out = @() 0..2047 | % {$out += Get-Random -Minimum 0 -Maximum 255} [System.IO.File]::WriteAllBytes("myrandomfiletest", $out)
-
-
more options (including some long, complicated scripts I couldn't understand):
-
Random.Next(): https://docs.microsoft.com/en-us/dotnet/api/system.random.next?view=net-5.0
-
good overview of many options: http://vcloud-lab.com/entries/powershell/microsoft-powershell-generate-random-anything-filename--temppath--guid--password-
-
performance:
- context of reproducing dd reading from /dev/random and writing to file: https://stackoverflow.com/questions/29501598/powershell-random-file-generator-too-slow
- compare powershell options for creating random data: https://communary.net/2015/04/16/get-random-vs-system-random/
-
https://stackoverflow.com/questions/533636/generating-random-files-in-windows
-
using fsutil (just writes 0s to file): https://www.windows-commandline.com/how-to-create-large-dummy-file/
-
generate lots of files in a loop (powershell): https://bielaszka.com/how-to-generate-large-files-with-random-content-in-powershell/
-
- Checking hash values for files in Powershell:
-
CertUtil -hashfile <file_path> <hash_algorithm>
-
WHERE:
- <file_path> = path to file you want to hash.
- <hash_algorithm> = hash algorithm (“sha256”, “md5”, etc.)
-
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-filehash
-
Example:
Get-FileHash <file_path> -Algorithm SHA256
-
WHERE:
-
-Algorithm
algorithms: “SHA256”, “MD5” (not included in Windows anymore, it seems), etc.
-
-
RETURNS: an object where "Hash" inside contains the actual hash value.
-
Notes:
- looks like no way to check a file against its previously generated hash (like in sha256sum).
- commandlet to check if you have the hash value and algorithm: https://www.christopherkeim.com/post/verify-file-hash-with-powershell
One uses Compress-Archive
and Expand-Archive
to create and unpack zip files in Powershell.
Tutorials:
- https://www.howtogeek.com/670314/how-to-zip-and-unzip-files-using-powershell/
- https://adamtheautomator.com/powershell-unzip/
-
Example compressing a directory including the root directory:
Compress-Archive -LiteralPath C:\Reference -DestinationPath C:\Archives\Draft.zip
-
Example uncompressing directory archive:
Expand-Archive -LiteralPath 'C:\Archives\Draft.Zip' -DestinationPath C:\Reference
- Examples of working with JSON: https://social.technet.microsoft.com/Forums/windowsserver/en-US/e5339aec-8ba0-4c4f-9286-88e93489c9ae/iterating-through-json-file-powershell
-
ConvertFrom-Json
documentation - https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/convertfrom-json?view=powershell-7.1 -
Pipe to
ConvertFrom-Json
to convert a stream of characters that contains JSON into a JSON object model that is easier to interact with.$config_json = Get-Content -Raw -LiteralPath "${config_file_path_in}" | ConvertFrom-Json
-
Usage, and good details on how it works under the hood: https://stackoverflow.com/questions/33520699/iterating-through-a-json-file-powershell
- note: it is converting to a strange type of object that isn't super-easy or intuitive to work with.
-
Good detailed notes on dealing with the PSObject instance(s) that come back from
ConvertFrom-Json
:-
includes code to convert from PSObject to Hash Table:
function ConvertPSObjectToHashtable { param ( [Parameter(ValueFromPipeline)] $InputObject ) process { if ($null -eq $InputObject) { return $null } if ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string]) { $collection = @( foreach ($object in $InputObject) { ConvertPSObjectToHashtable $object } ) Write-Output -NoEnumerate $collection } elseif ($InputObject -is [psobject]) { $hash = @{} foreach ($property in $InputObject.PSObject.Properties) { $hash[$property.Name] = ConvertPSObjectToHashtable $property.Value } $hash } else { $InputObject } } }
-
More notes:
- folks discussing the problem of PSObjects and hash tables that led to the good link above: https://social.technet.microsoft.com/Forums/windows/en-US/7413fa8b-cd3e-4150-9192-3b5863f88bd4/how-to-output-entire-content-of-json-nested-hash-table-in-poweshell
- multiple keys of same name result in list of the values: https://stackoverflow.com/questions/53564363/powershell-json-duplicate-keys-condense-values-to-single-array
Running an R program in a powershell script:
- First, know where your R install is (example: C:\Program Files\R\R-4.1.1) You'll need to be able to find the \bin\x64\ folder inside to use Rscript.exe to run scripts in powershell.
- potentially enable my unsigned powershell script to run:
set-executionpolicy unrestricted
- to run R script in shell: <path_to_bin_folder>\Rscript.exe <path_to_script>
Running a SAS program inside a powershell script:
-
many options: https://stackoverflow.com/questions/57729365/how-to-execute-a-sas-program-using-powershell
-
Use
Start-Process
to run a SAS program in batch mode:Start-Process SAS -ArgumentList "-sysin ""C:\Temp\myProgram.sas""" -Wait
-
WHERE:
-
-Wait
- tells Start-Process to block until process completes. -
-ArgumentList
- lets you pass arguments directly to the process.
-
-
Start-Process
doc: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/start-process?view=powershell-7.1 -
more info: https://adamtheautomator.com/start-process/
- includes link to
Invoke-Process
custom commandlet...
- includes link to
-
Documentation:
- https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/xcopy
- https://adamtheautomator.com/xcopy/
Arguments:
- /s = subdirectories
- /e = include empty folders
- /k = preserve read-only
- /r = copies read-only files
- /h = copies hidden and system files.
- /o = copies file ownership and DACL
- /x = copies file audit and SACL, also implies /o
- /y = suppresses prompting
- /z = copies over a network in restartable mode
- /b = copies symbolic link instead of files
- /i = create destination if it does not exist.
Standard xcopy
command most similar to rsync -avWH
:
xcopy a: b: /s /e /h /i /y /b /z # network "restartable" mode.
xcopy a: b: /s /e /h /i /y /b /x # NOT network "restartable" mode, instead maintain window security information.
Notes:
-
for "standard command", had to remove syncing windows equivalent of permissions (/x) because it can't copy security while in restartable mode (/z). Error:
- The /Z and /O (or /X) options conflict: cannot copy security in restartable mode.
Robocopy is an updated version of xcopy with many additional features. I think it works the same as xcopy for existing command strings...?
Documentation:
- https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy
- https://petri.com/robocopy-complete-guide/
Other notes:
- a - archive (-rlptgoD, no -AXUNH)
- v - verbose
- W - whole-file copy - not just deltas.
- H - preserve hard links...?
- (R) - relative path names (not important)
- r - recursive
- l - links - include symlinks.
- p - perms - copy permissions
- t - times - preserve modification times
- g - group - preserve group
- o - owner - preserve owner (super-user-only)
- D - --devices, --specials -- devices - preserve device files -- specials - preserve special files
rsync -avWH
-
if you open a powershell into a network share folder by holding the left shift key and right-clicking to get context menu, then choose "Open PowerShell window here", if your script relies on file paths, this could break your script. If you see the current folder as something like:
Microsoft.PowerShell.Core\FileSystem::\\<share_name>\<path>
-
This path won't work for some file path based command-lets, and you'll get message(s) about "unsupported protocol":
WARNING: Error creating FileStream or StreamWriter object for file name 'Microsoft.PowerShell.Core\FileSystem::\\<share_name>\<path>': Exception calling ".ctor" with "2" argument(s): "The given path's format is not supported."
-
To resolve this, open the share using it's drive letter, then navigate to that same folder, and all should work.
-