How to use path as parameter?
Solution 1
OK, I solved it myself.
First of all [string]$TarPath = "$PSScriptRoot"
doesn't work at all!
The variable is always empty.
However, my first idea was to define $TarPath
and leave it unchanged until it defined again. This turned out that doesn't work.
Here is my solution:
# Define The Target Path
Write-Host "Please enter Screenshot-Path"
$TarPath = Read-Host "Else the screenshot will be in $PWD"
if (!$TarPath) {$TarPath = $pwd}
If nothing is entered at the prompt $pwd
will be used.
Solution 2
Well, yes, that's about it, that approach will work. The only thing is that in the PROCESS
block, you re-assign once again your $TarPath
, making your fallback mechanism ineffective:
$TarPath = $PSScriptRoot
Delete that line and it will work like a charm.
Additionally, you could add validations such as making sure the parameter can be null, but not empty, and must be a valid path:
[ValidateScript({if ($_){ Test-Path $_}})]
[string]$TarPath = "$PSScriptRoot"
One last thing, if you want, as described in your question, to let the user use -Path
on the call, you can also add an alias to your Param
.
[Alias('Path')]
[ValidateScript({if ($_){ Test-Path $_}})]
[string]$Path = "$PSScriptRoot"
Solution 3
You redifine $TarPath
in your function body:
$TarPath = $PSScriptRoot
This unconditionally supersedes any value previously assigned to the parameter. Remove the line and you can pass the parameter like this:
Take-Screenshot -TarPath 'C:\some\folder'
or omit the parameter to leave it at its default value ($PSScriptRoot
).
I'd recommend to also change the line
$target = "$TarPath\screenshot-$stamp.png"
into this:
$target = Join-Path $TarPath "screenshot-$stamp.png"
so you don't need to fiddle around with trailing backslashes.
Function Take-Screenshot {
[CmdletBinding()]
Param(
[string]$Width,
[string]$Height,
[string]$TarPath = "$PSScriptRoot"
)
PROCESS {
[Reflection.Assembly]::LoadWithPartialName("System.Drawing") > $Null
# Changed how $bounds is calculated so that screen shots with multiple monitors that are offset work correctly
$bounds = [Windows.Forms.SystemInformation]::VirtualScreen
# Define The Target Path
$stamp = get-date -f MM-dd-yyyy_HH_mm_ss
$target = Join-Path $TarPath "screenshot-$stamp.png"
# Take the Screenshot
$bmp = New-Object Drawing.Bitmap $bounds.width, $bounds.height
$graphics = [Drawing.Graphics]::FromImage($bmp)
$graphics.CopyFromScreen($bounds.Location, [Drawing.Point]::Empty, $bounds.size)
$bmp.Save($target)
$graphics.Dispose()
$bmp.Dispose()
}
}
Addendum: There are two scenarios where defining the default value for the parameter -TarPath
as $TarPath = "$PSScriptRoot"
doesn't work:
The parameter is defined as a parameter to the script (not to a function within the script) and the script is run from CMD:
powershell -File 'C:\path\to\script.ps1'
The script is run with PowerShell v2. The variable was only available in modules prior to PowerShell v3.
In both scenarios "$PScriptRoot"
can be replaced with $PWD.Path
:
[CmdletBinding()]
Param(
[string]$Width,
[string]$Height,
[string]$TarPath = $PWD.Path
)
mr netlord
Updated on June 04, 2022Comments
-
mr netlord almost 2 years
I would like to have a screenshot tool in PS. Because I don't want to reinvent the wheel I searched and found a script at github (https://github.com/mikepruett3/psfetch), which I adapted for my needs.
Now I would like to change the behaviour - when the script is started with no parameter it should make a screenshot in the current directory. If the user enters a path (with
-Path
) the screenshot should be saved there.My idea was to define (in my case)
$Tarpath
and redefine it when the option is given. How to do this?Here is my actual script:
# PSFetch.ps1 # A Screenfetch writen in PowerShell # # ----------------------------------------------------------- # The Original Inspirations for CMDfetch: # ----------------------------------------------------------- # screenFetch by KittyKatt # https://github.com/KittyKatt/screenFetch # A very nice screenshotting and information tool. For GNU/Linux (Almost all Major Distros Supported) *This has been ported to Windows, link below.* # # archey by djmelik # https://github.com/djmelik/archey # Another nice screenshotting and information tool. More hardware oriented than screenFetch. For GNU/Linux # ----------------------------------------------------------- # # DONE: Function to Take the Screenshot Function Take-Screenshot { [CmdletBinding()] Param( [string]$Width, [string]$Height, [string]$TarPath = "$PSScriptRoot" ) PROCESS { [Reflection.Assembly]::LoadWithPartialName("System.Drawing") > $Null # Changed how $bounds is calculated so that screen shots with multiple monitors that are offset work correctly $bounds = [Windows.Forms.SystemInformation]::VirtualScreen # Check Path for Trailing BackSlashes # $TarPath = $PSScriptRoot if ( $TarPath.EndsWith("\") ) { $TarPath = $TarPath.Substring(0,$Path.Length-1) } # Define The Target Path $stamp = get-date -f MM-dd-yyyy_HH_mm_ss $target = "$TarPath\screenshot-$stamp.png" # Take the Screenshot $bmp = New-Object Drawing.Bitmap $bounds.width, $bounds.height $graphics = [Drawing.Graphics]::FromImage($bmp) $graphics.CopyFromScreen($bounds.Location, [Drawing.Point]::Empty, $bounds.size) $bmp.Save($target) $graphics.Dispose() $bmp.Dispose() } } # DONE: Fix support for Multiple Monitors # FROM: Shay Levy's Response - http://stackoverflow.com/questions/7967699/get-screen-resolution-using-wmi-powershell-in-windows-7 $ScreenWidth = 0 $ScreenHeight = 0 Add-Type -AssemblyName System.Windows.Forms $DisplayCount = [System.Windows.Forms.Screen]::AllScreens.Bounds.Count $Bounds = [System.Windows.Forms.Screen]::AllScreens | Select-Object -ExpandProperty Bounds $ScreenWidth = $Bounds | Measure-Object -Property Width -Sum | Select-Object -ExpandProperty Sum $ScreenHeight = $Bounds | Measure-Object -Property Height -Maximum | Select-Object -ExpandProperty Maximum $RESOLUTION = "$ScreenWidth x $ScreenHeight" # Take Screenshot if the Parameters are assigned... Take-Screenshot -Width $ScreenWidth -Height $ScreenHeight -TarPath $target
edit i forgot to remove the $tarpath int the PROCESS-block. It remained here from my first tests...
-
Ansgar Wiechers over 8 yearsPlease do not put tags in the subject. To mark a question as solved accept the answer that resolved the problem for you.
-
Kellen Stuart over 7 yearsBut reinventing the wheel is fun :p
-
-
mr netlord over 8 yearsIf i change it this was the script creates two screenshots... I tried ./psfetch -tarpath "D:\aaa" and it makes one in the actual dir and the second in D:\ (and not in the subdir)
-
Piyush over 8 yearsFirst, you're supposed to call any one of the
Take-Screenshot
with any one of the given approach. Either with 3 args or 2 args as per your need. In that way there will only be 1 screenshot generated. Next, forD:\aaa
can you confirm if the folder, aaa, already exist or not? -
Ansgar Wiechers over 8 yearsApparently you're using PowerShell v2.0. You should've mentioned that.
-
mr netlord over 8 yearsNo, Sir - i´m using the actual PS-Version... $PSVersionTable PSVersion 5.0.10105.0
-
Ansgar Wiechers over 8 yearsCan't reproduce.
$PSScriptRoot
works just fine in PowerShell v5 (5.0.10514.6) on Windows 10 or Server 2012 R2. -
Tydaeus about 5 years$PSScriptRoot doesn't exist during parameter initialization; it gets defined afterwards. I've worked around this by doing something like
if (-not $PSBoundParameters.ContainsKey('Path') { $Path = $PSScriptRoot }
after the startingparam
block, similar to Ansgar's answer. -
Kellen Stuart over 4 yearsI wonder if there's a better type than
[string]
for$TarPath
? Is there no object that represents a path in .NET? -
Ansgar Wiechers over 4 years@KolobCanyon You could use
[System.IO.FileInfo]
, but what would be the advantage? -
Kellen Stuart over 4 yearsIdk, maybe there's some built in validation or something useful in that class? I find myself having to write
if(-not (Test-Path $path)) { Write-Error "invalid path: $path" }
a lot -
Ansgar Wiechers over 4 yearsYou could use parameter validation for that. Besides, just because a path doesn't exist (yet) doesn't automatically mean it's invalid.