Check if function input is a System.IO.FileInfo or a string object? - powershell

This seems to work on my Terminal, It will correctly Identify the Object Type:
If ($var.getType().name -eq 'DirectoryInfo'){'File IO Obj'}else{'String Obj'}
But inside a function it falls apart:
Function Test{
param(
[Parameter(ValueFromPipeline, Position = 1)]
$InputObect
)
If ($input.getType().name -eq 'DirectoryInfo')Else{'Something Else'}
}
I tried this as well:
Function Test{
param(
[Parameter(ValueFromPipeline, Position = 1)]
$InputObect
)
$global:var = $InputObect
}
Checking on terminal the type of $Var with $var.getType().FullName returns System.Object[]
I have tried searching but this is all I could turn up.
I am simply looking to identify the type of file piped/sent in, then use If or Else to do something.
Any help would be greatly appreciated!

You can do it like this:
function Test {
Param (
[System.IO.FileInfo]$FileObject
)
Write-Host ($FileObject -eq $null)
if ($FileObject -eq $null) {
return $null
}
if ($FileObject.GetType() -eq [System.IO.FileInfo]) {
Write-Host ("Input is file object")
}
Write-Host ($FileObject.FullName)
}
Test -FileObject ([System.IO.FileInfo]::new("C:\users\user\documents\file.txt"))
Define the required type within in the parameter declaration to make it easy to understand what is required.
If you want to accepted multiple types you can do it like this:
function Test {
Param (
$IoObject
)
if ($IoObject -eq $null) {
return $null
}
if (($IoObject.GetType() -eq [System.IO.FileInfo]) -or ($IoObject.GetType() -eq [System.IO.DirectoryInfo])) {
Write-Host ("Input is file object or directory object")
}
}
You could also check for GetType().BaseType -eq [System.IO.FileSystemInfo] which should be true for Directories and Files as well.

Another way to address this use case.
Getting the object type directly - for example select the first object returned for a directory or a file:
# Directory
((Get-ChildItem -Path 'D:\Temp' -Directory)[0]).GetType().Name
# Results
<#
DirectoryInfo
#>
# File
((Get-ChildItem -Path 'D:\Temp' -File)[0]).GetType().Name
# Results
<#
FileInfo
#>
Then just directly compare the results in one line
# Folder and file
(((Get-ChildItem -Path 'D:\Temp' -Directory)[0]).GetType().Name).CompareTo(((Get-ChildItem -Path 'D:\Temp' -File)[0]).GetType().Name)
# Results
<#
-1
#>
File and file
(((Get-ChildItem -Path 'D:\Temp' -File)[0]).GetType().Name).CompareTo(((Get-ChildItem -Path 'D:\Temp' -File)[0]).GetType().Name)
# Results
<#
0
#>
As a full function
Clear-Host
Function Test-InputObjectType
{
<#
.Synopsis
Short description
.DESCRIPTION
Long description
# Use this as an example of case sensitive string matching
.EXAMPLE
Example of how to use this function
.EXAMPLE
Another example of how to use this function
#>
[CmdletBinding(SupportsShouldProcess)]
[Alias('tiot')]
param
(
[Parameter(ValueFromPipeline,
Position = 1)]
$InputObject
)
# Initialize GUI resources
# ...
# Required for use with web SSL sites
# ...
#region Begin Script ---
#
If ((((Get-ChildItem -Path $InputObject -Directory)[0]).GetType().Name) -eq 'DirectoryInfo')
{"$InputObject is of type DirectoryInfo"}
Else {"$InputObject is of type FileInfo"}
#
#endregion EndScript ---
}
Test-InputObjectType -InputObject 'D:\Temp'
# Results
<#
D:\Temp is of type DirectoryInfo
#>
Test-InputObjectType -InputObject 'D:\Temp\arp_IPA.txt'
# Results
<#
D:\Temp\arp_IPA.txt is of type FileInfo
#>

Related

I have a file organiser powershell script which runs without any error but it doesnt perform the moving operation

It is a file organiser script I wrote for myself. For a specific purpose of mine. Whenever I try to run it It runs and closes off. But the move operation is not happening. The below comments may help you understand what the code is doing.Please help me on what am i doing wrong here. I am extremely new to Powershell Scripting.
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
# Global variable declarations
$global:pathsFromConfig = Get-Content -Path $PSScriptRoot"\MoverPaths.txt"
$global:categoriesFromConfig = Get-Content -Path $PSScriptRoot"\MoverCategories.txt"
$global:categryHash = #{}
# Method call to read configs, create dirs, & move files
readCreateAndMoveFiles
# Method definition
function readCreateAndMoveFiles{
# Reads categories config.txt and splits them line by line
# Adds each line as a key value pair to a hashtable
foreach($category in $categoriesFromConfig)
{
$temp = $category -split ":"
$categryHash.add($temp[0].trim().toString(),($temp[1]).trim().toString())
}
# For each category in the hash table, calls create directory method, and then moves the files based on current category
foreach($hashItem in $categryHash.GetEnumerator()){
# Creates a directory with the Hash Key
Foreach($pathToMonitor in $pathsFromConfig){
$categoryFullPath = $pathToMonitor+$hashItem.Name
createDirectory($categoryFullPath)
# Moves files into that directory
Set-Location -Path $pathToMonitor
$extentions = $hashItem.Value
Get-Item $extentions | Move-Item -Destination $categoryFullPath
$categoryFullPath = ""
}
}
}
# Method Definition
function createDirectory ($categoryName)
{
if(Test-Path -Path $categoryName)
{
# Directory already Exists!
}
else
{
# Creates Directory
md $categoryName
}
}
The config files are hereby:
MoverCategories.txt
Images:*.jpg,*.jpeg,*.png,*.tiff,*.raw,*.heic,*.gif,*.svg,*.eps,*.ico
Documents:*.txt,*.pdf,*.doc,*.docx,*.xls,*.xlsx,*.ppt,*.pptx,*.html,*.xls,*.csv,*.rtx
MoverPaths.txt
D:\Downloads\
Found a way to do this. Thanks for all of your input. Now the script moves files. Instead of sending all extentions in a single shot, i made it into an array and sent it one by one. Now it works fine. If you guys could help me reduce the time of execution that would be great.But the code works now I am happy.
foreach($hashItem in $categryHash.GetEnumerator()){
# Creates a directory with the Hash Key
Foreach($pathToMonitor in $pathsFromConfig){
$categoryFullPath = $pathToMonitor+$hashItem.Name
createDirectory($categoryFullPath)
# Moves files into that directory
[String[]]$extentions = #()
$extentions = $hashItem.Value -split ','
foreach($string in $extentions)
{
Get-Item $pathToMonitor\* -Include $string | Move-Item -Destination $categoryFullPath
}
}
}
Try this
#specify path(s)
$path = "$env:USERPROFILE\Downloads"
## this is make an array of the extensions in the foloder
$extensions = Get-ChildItem -Path $path | Select-Object -Unique -Property #{label = 'ext'
expression = { $_.Extension.substring(1) }
}
## this function will
function New-FoldersByName {
param
(
[Parameter(Mandatory = $true, ValueFromPipeline = $true, HelpMessage = 'Data to process')]
$InputObject
)
process {
Set-Location $path
if (!(Test-Path -PathType Container $InputObject )) {
New-Item -ItemType directory -Name $InputObject.ext -WhatIf
Write-Host -Message "A folder named $($InputObject.ext) does not exist. Creating..."
}
else {
Write-Host -Message "A folder named $($InputObject.ext) already exists. Skipping..."
}
}
}
##this is a reuseable function to moves items in a folder into a subfolder named after the files extension
## if extension is .exe the file with be moved to ./EXE/filename.exe
function Move-ItemsByName {
param
(
[Parameter(Mandatory = $true, ValueFromPipeline = $true, HelpMessage = 'Data to process')]
$InputObject
)
process {
Set-Location -Path $path
Move-Item -Path ('*.{0}' -f $InputObject.ext) -Destination ('{0}' -f $InputObject.ext) -WhatIf
}
}
$extensions | New-FoldersByName
$extensions | Move-ItemsByName

How to find a specific text is available in .txt file using powershell

I want to write a PowerShell script where I will give two string values as parameters, It should check the .txt file and should tell whether the strings are available or not in the given file. For example, if I have a list of employees details. I will give the emp_id and emp_name as input. If the name and id exist in that .txt file it should print that. If not it should print the else statement.
Function Empdetails {
[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter(Mandatory=$true)]$empid,
[Parameter(Mandatory=$true)]$empname)
$path = Get-Content C:\empdetails.txt | Where-Object {$_ -like '*name*'}
if ($path -eq $true) {
Write-Host "Found"
}
else {
Write-Host "Not Found"
}
}
I tried the above code, But it is working. Could you please help me to figure it out?
You have two parameters but you are not using them in your function, since it's not clear which parameter should be used for the file path and which for the word you're searching for in the file, I have changed the parameter names for something more explanatory.
Also note, the result of below expression will be either an array of strings, a single string or $null:
$path = ... | Where-Object {$_ -like '*name*'}
Hence, your if condition if ($path -eq $true) can never be met unless $path has assigned the literal string True. If, however, you change the order of the condition to $true -eq $path, then the condition can be met and will be $true as long as $path is not $null / empty string.
$content = 'something'
$content -eq $true # => False
$true -eq $content # => True
$content = 'True'
$content -eq $true # => True
$true -eq $content # => True
From equality operators:
The equality operator can compare objects of different types. It is important to understand that the value is on the right-hand side of the comparison can be converted to the type of the left-hand side value for comparison.
Function Empdetails {
[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter(Mandatory=$true)]$FilePath,
[Parameter(Mandatory=$true)]$WordToSearch
)
$content = Get-Content $FilePath | Where-Object {$_ -like "*$wordToSearch*"}
if ($content) {
# if `$content` is populated, use return to end the function here
return Write-Host "Found"
}
Write-Host "Not Found"
}
Empdetails -FilePath ./path/to/file.ext -WordToSearch somekeyword

Function from imported script doesn't execute

i write a script with a function.
here is the script with the function:
function GenerateHashesForProjects(){
[array]$result=#()
Write-Output "Genrate Hash Values"
$dependencyFolder = Join-Path -Path $PSScriptRoot -ChildPath "..\..\Sources\_Dependencies"
#get all folder in a list below the dependency folder expect the "Modules" folder
$dependencyContent = Get-ChildItem -Path $dependencyFolder | where {$_.PSIsContainer -and ($_.Name -notlike "*Modules*")}
#Fill the result array with the project file name and the depending hash value of this file
foreach ($item in $dependencyContent) {
$denpencyProjects = Get-ChildItem -Path $item.Fullname | where { ($_ -like "*.csproj") }
$hashValue = (Get-FileHash $denpencyProjects.FullName -Algorithm MD5).Hash
$name = $denpencyProjects.Name
Write-Output "name: $name `nvalue: $hashValue"
$result += #($denpencyProjects.Name, $hashValue)
}
return $result
}
That script works fine.
Now i want to use this function also in another script. So i import the script and define a variable with that function. Here is the issue if a call the function without the variable it works fine but with the variable definition not, why?
Here is the second script with the import:
. Join-Path -Path $PSScriptroot -ChildPath "..\..\Build\Tools\GenerateHashesForProjects.ps1"
[array]$dependencyFileValues = GenerateHashesForProjects
This test works fine:
. Join-Path -Path $PSScriptroot -ChildPath "..\..\Build\Tools\GenerateHashesForProjects.ps1"
GenerateHashesForProjects
since you didn't post any responses to questions [grin], here is one way to rewrite your code.
what it does ...
creates an advanced function
uses the recommended name format for such
does not supply the "otta be there" Comment Based Help [grin]
defines the parameters
only the $Path is required.
defines but does not use a begin {} block
defines a process {} block
grabs a list of the dirs that branch from the source path
filters out the dirs that are in the $ExcludeDirList
gets the files in those dirs that match the $FileFilter
iterates thru that list
builds a [PSCustomObject] for each file with the desired details
you can add or remove them as needed.
sends that PSCO out to the calling code
the line that calls the function stores the entire set of results into the $Result variable and then shows that on screen.
a few notes ...
i had to change a lot of your details since i have no csproj files
there are no "what is happening" lines
if you need that, you can easily add such. i would NOT use Write-Output, tho, since that will pollute your output data.
there is no error detection OR error handling
here's the code ...
function Get-ProjectFileHash
{
<#
CommentBasedHelp goes here
#>
[CmdletBinding ()]
Param
(
[Parameter (
Mandatory,
Position = 0
)]
[string]
$Path,
[Parameter (
Position = 1
)]
[ValidateSet (
'MD5',
'MACTripleDES',
'RIPEMD160',
'SHA1',
'SHA256',
'SHA384',
'SHA512'
)]
[string]
$Algorithm = 'MD5',
[Parameter (
Position = 2
)]
[string[]]
$ExcludeDirList,
[Parameter (
Position = 3
)]
[string]
$FileFilter
)
begin {}
process
{
$ProjDirList = Get-ChildItem -LiteralPath $Path -Directory |
Where-Object {
# the "-Exclude" parameter of G-CI is wildly unreliable
# this avoids that problem [*grin*]
# build a regex OR listing to exclude
$_.Name -notmatch ($ExcludeDirList -join '|')
}
$FileList = Get-ChildItem -LiteralPath $ProjDirList.FullName -File -Filter $FileFilter
foreach ($FL_Item in $FileList)
{
[PSCustomObject]#{
FileName = $FL_Item.Name
DirName = $FL_Item.Directory
Algorithm = $Algorithm
Hash = (Get-FileHash -LiteralPath $FL_Item.FullName -Algorithm $Algorithm).Hash
}
}
}
end {}
} # end >>> function Get-ProjectFileHash
$Source = 'C:\ProgramData\chocolatey\lib'
$NotWanted = 'choco', '7zip', 'kb', 'bad', 'bkp'
$Filter = '*.nupkg'
$Result = Get-ProjectFileHash -Path $Source -Algorithm MD5 -ExcludeDirList $NotWanted -FileFilter $Wanted
$Result
truncated output ...
FileName DirName Algorithm Hash
-------- ------- --------- ----
autohotkey.nupkg C:\ProgramData\chocolatey\lib\autohotkey MD5 35A1B894AEA7D3473F3BBCBF5788D2D6
autohotkey.install.nupkg C:\ProgramData\chocolatey\lib\autohotkey.install MD5 EFE8AD812CBF647CFA116513AAD4CC15
autohotkey.portable.nupkg C:\ProgramData\chocolatey\lib\autohotkey.portable MD5 D31FA1B5496AAE266E4B0545835E9B19
[*...snip...*]
vcredist2015.nupkg C:\ProgramData\chocolatey\lib\vcredist2015 MD5 56321731BC0AEFCA3EE5E547A7A25D5E
vlc.nupkg C:\ProgramData\chocolatey\lib\vlc MD5 8177E24675461BDFF33639BF1D89784B
wiztree.nupkg

Powershell - Variable problems

I wrote a script that will pull data from a .properties file (basically a config file). Some of the data from the properties file has environment data (i.e. %UserProfile%), so I run it through a function (Resolve–EnvVariable) that will replace the environment variable with the actual value. The replace works perfectly, but somehow the data seems to be altered.
When I try to use the values that have been run through the function, they no longer work (see results down below).
This is the file contents of c:\work\test.properties
types="*.txt"
in="%UserProfile%\Downloads"
This is my PowerShell Script
Clear-Host
#Read the properties file and replace the parameters when specified
if (Test-Path C:\work\test.properties) {
$propertiesFile = Get-Content C:\work\test.properties
Write-Host "Parameters will be substituded from properties file" -ForegroundColor Yellow
foreach ($line in $propertiesFile) {
Write-Host ("from Properties file $line")
$propSwitch = $line.Split("=")[0]
$propValue = Resolve–EnvVariable($line.Split("=")[1])
switch ($propSwitch) {
"types" { $types = $propValue }
"in" { $in = $propValue }
}
}
}
write-host ("After running through function `n in=" + $in + "<- types=" + $types + "<-")
# This function resolves environment variables
Function Resolve–EnvVariable {
[cmdletbinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True, Mandatory = $True,
HelpMessage = "Enter string with env variable i.e. %APPDATA%")]
[ValidateNotNullOrEmpty()]
[string]$String
)
Begin {
Write-Verbose "Starting $($myinvocation.mycommand)"
} #Begin
Process {
#if string contains a % then process it
if ($string -match "%\S+%") {
Write-Verbose "Resolving environmental variables in $String"
#split string into an array of values
$values = $string.split("%") | Where-Object { $_ }
foreach ($text in $values) {
#find the corresponding value in ENV:
Write-Verbose "Looking for $text"
[string]$replace = (Get-Item env:$text -erroraction "SilentlyContinue").Value
if ($replace) {
#if found append it to the new string
Write-Verbose "Found $replace"
$newstring += $replace
}
else {
#otherwise append the original text
$newstring += $text
}
} #foreach value
Write-Verbose "Writing revised string to the pipeline"
#write the string back to the pipeline
Write-Output $NewString
} #if
else {
#skip the string and write it back to the pipeline
Write-Output $String
}
} #Process
End {
Write-Verbose "Ending $($myinvocation.mycommand)"
} #End
} #end Resolve-EnvVariable
# Hardcoded values work
$test1 = Get-ChildItem -Path "C:\Users\Paul\Downloads" -Recurse -Include "*.txt"
# Values pulled and updated through function do not work
$test2 = Get-ChildItem -Path $in -Recurse -Include $types
# If I manually assign the values, it works
$in = "C:\Users\Paul\Downloads"
$types = "*.txt"
$test3 = Get-ChildItem -Path $in -Recurse -Include $types
foreach ($test in $test1) { write-host "test1 $test" }
foreach ($test in $test2) { write-host "test2 $test" }
foreach ($test in $test3) { write-host "test3 $test" }
Results
Parameters will be substituded from properties file
from Properties file types="*.txt"
from Properties file in="%UserProfile%\Downloads"
After running through function
in="C:\Users\Paul\Downloads"<- types="*.txt"<-
test1 C:\Users\Paul\Downloads\Test\testPaul.txt
test1 C:\Users\Paul\Downloads\Test2\File1.txt
test3 C:\Users\Paul\Downloads\Test\testPaul.txt
test3 C:\Users\Paul\Downloads\Test2\File1.txt
Two alternatives:
1. Use Environment.ExpandEnvironmentVariables()
If you switched to non-qualified string values and escaped your \, it would be as simple as piping the file to ConvertFrom-StringData, at which point you could expand the variable values with Environment.ExpandEnvironmentVariables():
Properties file:
types=*.txt
in=%UserProfile%\\Downloads
Script:
# Convert file to hashtable
$properties = Get-Content file.properties -Raw |ConvertFrom-StringData
# Copy value to new hashtable, but expand env vars first
$expanded = #{}
foreach($entry in $properties.GetEnumerator()){
$expanded[$entry.Key] = [Environment]::ExpandEnvironmentVariables($entry.Value)
}
Should give you the desired values:
PS C:\> $expanded
Name Value
---- -----
in C:\Users\username\Downloads
types *.txt
2. Use and dot-source a PowerShell script for your properties
This is lifted straight out of a page of the original Exchange Server modules - place all configuration variables in separate scripts, which are in turn dot-sourced when initializing a new session:
Properties file:
$types = "*.txt"
$in = Join-Path $env:USERPROFILE Downloads
Script:
# dot source the variables
. (Join-Path $PSScriptRoot properties.ps1)
# do the actual work
Get-ChildItem $in -Include $types

trying to specify the file path

I am trying to specify my file path in the script that I got from here: https://gallery.technet.microsoft.com/scriptcenter/Outputs-directory-size-964d07ff
The current file path points to the directory, but I am unable to locate the variable that I need to change in order to specify a different path.
# Get-DirStats.ps1
# Written by Bill Stewart (bstewart#iname.com)
# Outputs file system directory statistics.
#requires -version 2
<#
.SYNOPSIS
Outputs file system directory statistics.
.DESCRIPTION
Outputs file system directory statistics (number of files and the sum of all file sizes) for one or more directories.
.PARAMETER Path
Specifies a path to one or more file system directories. Wildcards are not permitted. The default path is the current directory (.).
.PARAMETER LiteralPath
Specifies a path to one or more file system directories. Unlike Path, the value of LiteralPath is used exactly as it is typed.
.PARAMETER Only
Outputs statistics for a directory but not any of its subdirectories.
.PARAMETER Every
Outputs statistics for every directory in the specified path instead of only the first level of directories.
.PARAMETER FormatNumbers
Formats numbers in the output object to include thousands separators.
.PARAMETER Total
Outputs a summary object after all other output that sums all statistics.
#>
[CmdletBinding(DefaultParameterSetName="Path")]
param(
[parameter(Position=0,Mandatory=$false,ParameterSetName="Path",ValueFromPipeline =$true)]
$Path=(get-location).Path,
[parameter(Position=0,Mandatory=$true,ParameterSetName="LiteralPath")]
[String[]] $LiteralPath,
[Switch] $Only,
[Switch] $Every,
[Switch] $FormatNumbers,
[Switch] $Total
)
begin {
$ParamSetName = $PSCmdlet.ParameterSetName
if ( $ParamSetName -eq "Path" ) {
$PipelineInput = ( -not $PSBoundParameters.ContainsKey("Path") ) -and ( -
not $Path )
}
elseif ( $ParamSetName -eq "LiteralPath" ) {
$PipelineInput = $false
}
# Script-level variables used with -Total.
[UInt64] $script:totalcount = 0
[UInt64] $script:totalbytes = 0
# Returns a [System.IO.DirectoryInfo] object if it exists.
function Get-Directory {
param( $item )
if ( $ParamSetName -eq "Path" ) {
if ( Test-Path -Path $item -PathType Container ) {
$item = Get-Item -Path $item -Force
}
}
elseif ( $ParamSetName -eq "LiteralPath" ) {
if ( Test-Path -LiteralPath $item -PathType Container ) {
$item = Get-Item -LiteralPath $item -Force
}
}
if ( $item -and ($item -is [System.IO.DirectoryInfo]) ) {
return $item
}
}
# Filter that outputs the custom object with formatted numbers.
function Format-Output {
process {
$_ | Select-Object Path,
#{Name="Files"; Expression={"{0:N0}" -f $_.Files}},
#{Name="Size"; Expression={"{0:N0}" -f $_.Size}}
}
}
# Outputs directory statistics for the specified directory. With -recurse,
# the function includes files in all subdirectories of the specified
# directory. With -format, numbers in the output objects are formatted with
# the Format-Output filter.
function Get-DirectoryStats {
param( $directory, $recurse, $format )
Write-Progress -Activity "Get-DirStats.ps1" -Status "Reading
'$($directory.FullName)'"
$files = $directory | Get-ChildItem -Force -Recurse:$recurse | Where-
Object
{ -not $_.PSIsContainer }
if ( $files ) {
Write-Progress -Activity "Get-DirStats.ps1" -Status "Calculating
'$($directory.FullName)'"
$output = $files | Measure-Object -Sum -Property Length | Select-Object
`
#{Name="Path"; Expression={$directory.FullName}},
#{Name="Files"; Expression={$_.Count; $script:totalcount += $_.Count}},
#{Name="Size"; Expression={$_.Sum; $script:totalbytes += $_.Sum}}
}
else {
$output = "" | Select-Object `
#{Name="Path"; Expression={$directory.FullName}},
#{Name="Files"; Expression={0}},
#{Name="Size"; Expression={0}}
}
if ( -not $format ) { $output } else { $output | Format-Output }
}
}
... the rest of the code did not seem relevant
You either specify the $Path variable when calling the script, or add a line that overrides the default value. I've highlighted where this is below.
[CmdletBinding(DefaultParameterSetName="Path")]
param(
[parameter(Position=0,Mandatory=$false,ParameterSetName="Path",ValueFromPipeline =$true)]
$Path=(get-location).Path, ################ PATH IS SET HERE ##############
[parameter(Position=0,Mandatory=$true,ParameterSetName="LiteralPath")]
[String[]] $LiteralPath,
[Switch] $Only,
When calling script:
C:>.\myscript.ps1 -Path "c:\temp"
what you call the value depends on where you call it from.
the "main" part of this cmdlet accepts one of a couple parameters; path and literalPath, path would be used in preference to literal path. If neither is specified the current working directory will be the starting point. passing different arguments to the cmdlet seems to be the easiest technique. The author's intended usage.
BUT...
Up in that first function, after the parameters are bound in the "begin" section... the actual path is "$item".
inside Get-DirectoryStats it's being referred to as $directory.
There are places where it's referred to as $_.
There are a lot of articles on the topic of "scope". here's one: https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_scopes