Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/cavo789/powershell_tips

Tips and tricks and snippets of PowerShell functions
https://github.com/cavo789/powershell_tips

powershell powershell-scripts powershell-tips

Last synced: about 1 month ago
JSON representation

Tips and tricks and snippets of PowerShell functions

Awesome Lists containing this project

README

        



# PowerShell tips and snippets

![Banner](./banner.svg)

> A few PowerShell tips and functions snippets

* [Core features](#core-features)
* [Syntax elements](#syntax-elements)
* [Define global variables](#define-global-variables)
* [Include external script](#include-external-script)
* [Split long string on multiple lines](#split-long-string-on-multiple-lines)
* [Variables](#variables)
* [Some helpers](#some-helpers)
* [Files](#files)
* [Check if file exists](#check-if-file-exists)
* [Get a flat list of files](#get-a-flat-list-of-files)
* [Get the depth too](#get-the-depth-too)
* [Get list of files, recursively](#get-list-of-files-recursively)
* [Folders](#folders)
* [Get parent folder](#get-parent-folder)
* [Get the target filename of a symlink](#get-the-target-filename-of-a-symlink)
* [Prettify JSON](#prettify-json)
* [Consume XML response](#consume-xml-response)
* [Regex](#regex)
* [Between two tags](#between-two-tags)
* [Case insensitive](#case-insensitive)
* [Match all occurrences](#match-all-occurrences)
* [Multiline](#multiline)
* [License](#license)

## Core features

### Syntax elements

#### Define global variables

Defining a variable for the entire script; initialize it once and use it everywhere.

The key is to use the `$global:` prefix like below:

```powershell
begin {

# Folder where the running Powershell script is stored
$global:scriptDir = ""

function initialize() {
$global:scriptDir = Split-Path $script:MyInvocation.MyCommand.Path
}

function someFunction() {
Write-Host $global:scriptDir
}
}
```

#### Include external script

Including an external script (f.i. `my_helper.ps1`) can be done using the dot notation: `. my_helper.ps1`.

```powershell
# Define the debug mode constant
set-variable -name DEBUG -value ([boolean]$FALSE) -option Constant

function include_helpers() {

# List of helpers to load
$helpers = @("files", "images", "markdown")

if ($DEBUG -eq $TRUE) {
# If debug mode is enabled, debug.ps1 will also be loaded
$helpers = $helpers + @("debug")
}

foreach ($helper in $helpers) {
# Suppose that files are in the helpers sub folder
$filename = ".\helpers\$helper.ps1"

try {
if ([System.IO.File]::Exists($filename)) {
# The file exists; load it
. $filename
}
}
catch {
Write-Error "Error while loading helper $filename"
}
}

return;
}
```

Note: functions should be declare with the `global:` prefix.

#### Split long string on multiple lines

Don't write things like:

```powershell
Write-Error "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab ...")
```

but split the string using the `$( ...)` syntax:

```powershell
Write-Error $("Sed ut perspiciatis unde omnis iste natus error " +
"sit voluptatem accusantium doloremque laudantium, totam rem "
"aperiam, eaque ipsa quae ab ...")
```

For commands too, don't write things like:

```powershell
$target = Get-Item -Path $filename | Select-Object -ExpandProperty Target | Select-Object -First 1
```

but split the command like this:

```powershell
$target = Get-Item -Path $filename `
| Select-Object -ExpandProperty Target `
| Select-Object -First 1
```

### Variables

| Variable | Description |
| ------------------------------ | ------------------------------------------------------------------------- |
| `[string](Get-Location)` | Get the current folder. |
| `$MyInvocation.MyCommand.Name` | Return the full name of the running script (return f.i. `c:\temp\a.ps1`). |
| `$PSScriptRoot` | Return the folder of the running script (`c:\temp`) |

## Some helpers

### Files

#### Check if file exists

```powershell
<#
.SYNOPSIS
Check if a file exists on disk
.PARAMETER Filename
Name of the file to check
.OUTPUTS
True if the file exists,
False othwerise
#>
function fileExists([string] $filename) {
return [Boolean](Test-Path $filename -PathType Leaf)
}
```

Alternative solution: `[System.IO.File]::Exists($filename)` (no support for wildcard characters)

#### Get a flat list of files

The function below will retrieve the list of all files below the current running folder and will returns a flat list (i.e. only files name).

The function support exclusions like skipping specific folders or files.

```powershell
<#
.DESCRIPTION
Retrieve the list of all files (based on the mentioned pattern).
The result will be a flat list (i.e. only files name).
Support exclusions like skipping folders or files.
.PARAMETER Pattern
Pattern for files like c:\temp\*.* or just *.*
.PARAMETER Exclude
It's a regex that contains patterns to exclude
For instance, exclude some folders
".*\\\.config\\|.*\\\.git\\|.*\\backup\\"
And exclude files based on their extensions
".bmp$|.gif$|.ico$|.jpe?g$|.png$"
.OUTPUTS
Array
#>
function getListOfFiles([string] $pattern = "*.*", [string] $exclude = "") {
# Get the list of all files, retrieve a flatlist and
# don't report errors when f.i. a folder is a symlink
$files = Get-ChildItem . -Filter $pattern -Recurse `
| Where-Object { $_.Fullname -notmatch $exclude } `
| Group-Object "FullName" `
| Select-Object "Name"

return $files
}

# Sample
# Skip .git and backups folders
$exclude = ".*\\\.git\\|.*\\backups\\"
# and skip some extensions
$exclude += "|.bmp$|.gif$|.ico$|.jpe?g$|.png$"

$files = getListOfFiles '*.*' $exclude

foreach ($file in $files) {
Write-Host "Process" $file.Name
}
```

This will return something like below:

```text
C:\Christophe\demo\readme.md
C:\Christophe\demo\03_Annex\index.md
C:\Christophe\demo\03_Annex\01_Annex1\index.md
C:\Christophe\demo\07_Date\index.md
```

##### Get the depth too

The following snippet calculate a FolderDepth column based on the number of `\` separator in the filename:

```powershell

### Folders

#### Get parent folder

```powershell
<#
.SYNOPSIS
Return the parent folder of a file
.PARAMETER Filename
Filename for which the parent folder should be returned
.OUTPUTS
String
#>
function getParentFolderName([string] $filename) {
return (Split-Path -Path $filename)
}
```

#### Get the target filename of a symlink

When a file is a symbolic or hard link, the following function will return the original filename.

For instance if `c:\temp\a.ps1` is symlink to `c:\christophe\repositories\tools\utils.ps1`, the following function will return the full name of the original filename so will return `c:\christophe\repositories\tools\utils.ps1`.

```powershell
<#
.DESCRIPTION
When a file is a symlink, return the target path i.e. the original path of the file
Note: when the same file is symlinked multiple times, the ExpandProperty
will return all files so get only the first item which is the original file
.PARAMETER Filename
That file should be a symlink (hard or symbolic)
.OUTPUTS
String
#>
function getSymLinkTargetPath([string] $filename) {
$target = Get-Item -Path $filename `
| Select-Object -ExpandProperty Target `
| Select-Object -First 1

return [string]$target
}
```

#### Prettify JSON

```powershell
$objJSON = Get-Content -Path ".\test.json"
$objJSON | ConvertFrom-Json | ConvertTo-Json | Out-File -FilePath ".\test-Pretty.json"
Get-Content ".\test-Pretty.json"
```

#### Consume XML response

Consume a service returning XML and display the response

```powershell
[int] $Amount = 5
[string] $Type = "pargraphs"
[string] $Start = "yes"

[xml]$Temp = Invoke-WebRequest -UseBasicParsing -Uri "https://www.lipsum.com/feed/xml?amount=$Amount&what=$Type&start=$Start"

$Temp.feed.lipsum
```

## Regex

> [List of modifiers](https://www.regular-expressions.info/modifiers.html)

### Between two tags

Retrieve the content between two HTML tags:

```powershell

# Search for the pattern like:
#
# CONTENT
#
#
# Can be on the same line or on multiple lines
$content = "Ipso Lorem"

$pattern = [regex]$(
"\n?\<\!\-\- start \-\-\>" +
"([\s\S]*?)" +
"\<\!\-\- end \-\-\>\n?"
)

# Write the CONTENT
Write-Host $([string]($pattern).Match($content).groups[1].value)
```

### Case insensitive

The default mode is case sensitive, to change this, the mode to use is `(?i)` at the very start of the `[regex]` expression:

```powershell
$content = "Ipso lorem`n`n@TOdo Christophe: do this.`n`nIpso lorem"

$pattern = [regex]"(?msi)^(\@todo ([^\n\r]*))"

if (($pattern.Match($content)).success) {
Write-Warning "@TODO found at the beginning of the string"
Write-Host "There is a TODO for $($pattern.Match($content).groups[2].value)"
}
```

### Match all occurrences

Process every occurrences:

```powershell
$content = "# Heading 1`n`n## Heading 1.1`n`n## Heading 1.2`n`n## Heading 1.3"

# Skip heading 1 so start at 2
# (?ms) ==> Multi-lines regex
# ^ ==> The start of the line
# (\#{2,}) ==> We need to find at least two consecutive #
# ([^\n\r]*) ==> Capture the end of the line (exclude CRLF)
# $ ==> End of the line
$pattern = [regex]"(?ms)^(\#{2,}) ([^\n\r]*)$"

$headings = $pattern.Matches($content);

if ($headings.Count -gt 0) {
$match = $pattern.Match($content)

while ($match.Success) {
# Isolate the # (one or more) and the title
$pattern = [regex]"(#{2,})* (.*)"

# Display "Heading 1.1", "Heading 1.2", ...
Write-Host $pattern.Match($match).Groups[2].Value

$match = $match.NextMatch()
}
}
```

### Multiline

To make a search on more than one line:

```powershell

# Match a YAML block
#
# The block start with --- on his own line
# Then there is a content (one or more line)
# The block ends with --- on his own line

$content = "---`nTitle: My great title`n---`n"

$pattern = [regex]"(?ms)(^\-{3}([\s\S]*\s)^\-{3})"

# Write the YAML content
Write-Host $([string]($pattern).Match($content).groups[2].value)
```

## License

[MIT](./../LICENSE)