%~dp0 equivalent in powershell (using Expand-Archive cmdlet)

19,131

Solution 1

From your description, I gather that Fubar.zip and Unzip.ps1 are in the same directory. We'll pretend this directory is C:\Users\Me\Temp; although I understand that may vary.

Powershell's working directory will be the directory you're in (if called from CMD) when you launch; otherwise, it'll be from $env:UserProfile. Since the .bat file always call Unzip.ps1 from the directory that it's in (C:\Users\Me\Temp), powershell.exe will find it with this command (you can still use %~dp0 here; it's not hurting anything):

powershell.exe -ExecutionPolicy RemoteSigned -File Unzip.ps1

Inside of Unzip.ps1, you'll use Get-Location:

Expand-Archive -Force "$(Get-Location)\Fubar.zip" -dest c:\TEST\

However, if the .bat file does a cd into another directory, this won't work. From your %~dp0UNZIP.ps1 example, I assume this isn't the case, but let's address it anyway. If this is the case, you need to process from where the location of the script is. So for this call the full/relational path to the .ps1:

powershell.exe -ExecutionPolicy RemoteSigned -File C:\Users\Me\Temp\Unzip.ps1

Then, your Unzip.ps1 will need to look like this:

Expand-Archive -Force "${PSScriptRoot}\Fubar.zip" -dest 'C:\TEST\'

Alternatively, you can also do some fancy path splitting, as @JosefZ suggested. The $PSCommandPath and $MyInvocation variables contain the full path to your script; which you should familiarize yourself with:

$location = Split-Path $PSCommandPath -Parent
$location = Split-Path $MyInvocation.MyCommand.Path -Parent
Expand-Archive -Force "${location}\Fubar.zip" -dest 'C:\TEST\'

Note: Of course, you wouldn't set $location twice. I'm just showing you two ways to set it.

I hope this helps!

Solution 2

The equivalent of cmd.exe's %dp0 in PowerShell v3+ is $PSScriptRoot:

Both constructs expand to the absolute path of the folder containing the batch file / PowerShell script at hand.
(The only difference is that %dp0 contains a trailing \, unlike $PSScriptRoot).

Thus, to make your Unzip.ps1 script reference Fubar.zip in the same folder that the script itself is located in, use:

Expand-Archive -Force $PSScriptRoot\Fubar.zip -dest c:\TEST\
  • Constructing the path as $PSScriptRoot\Fubar.zip - i.e., blindly joining the variable value and the filename with \ - is a pragmatic shortcut for what is more properly expressed as (Join-Path $PSScriptRoot Fubar.zip). It is safe to use in this case, but see the comments for a discussion about when Join-Path should be used.

  • The automatic $PSScriptRoot variable requires PS v3+; in v2-, use
    Expand-Archive -Force ((Split-Path $MyInvocation.Mycommand.Path) + '\Fubar.zip') -dest c:\TEST\

Share:
19,131
Admin
Author by

Admin

Updated on June 18, 2022

Comments

  • Admin
    Admin almost 2 years

    I'm pretty new to scripting (especially powershell) and new to Stack Overflow, so please, excuse my ignorance and please bear with me! I will do my best to specifically explain what I'm looking to do and hopefully someone can give a detailed response of what I could do to make it work..

    Intended Process/Work Flow: A co-worker downloads "Install.zip" file that has all the necessary files. This "Install.zip" file contains "Setup.bat" file (for computer config), "Fubar.zip" file, 2 powershell scripts, and a custom powerplan (.pow) file. Once downloaded they will run the "Setup.bat" file and it will pretty much do all the work. Inside that batch file it calls 2 powershell scripts. 1)"Download.ps1" - Downloads some other files from the web. 2.) "Unzip.ps1" - Unzips "Fubar.zip" and places contents in another folder - C:\TEST\

    Issue: I've recently gotten familiar with using %~dp0 in batch files. I want to make sure that the location where my co-worker initially downloads the Install.zip doesn't throw off my batch file. So for example.. some people will download .zip files to the "Downloads" folder, then extract contents to proper destination. Others will download the .zip to a specific folder, then extract it within that folder. [Ex: C:\Alex\Install.zip --Extraction-- C:\Alex\Install\((Content))] So I tried to not pre-define file locations due to the variables. I've gotten the %~dp0 to work everywhere I need it to in my batch file. The only issue I have is getting my powershell scripts to use same working directory that my batch file is in. *My batch file and my powershell scripts will always be in the same directory. (Wherever that may be)

    Goal: I want my powershell script ("Unzip.ps1") to look for my "Fubar.zip" file in the same directory that its currently running in. (Again - Wherever that may be) I basically want to remove any variables that may throw off the powershell script. I want it to always use it's current working directory to locate Fubar.zip. I basically need powershell to either use its current working directory OR figure out a way to have it pull its current working directory and use that to look for "Fubar.zip".

    my current "Unzip.ps1" powershell script is extremely basic.

    Unzip.ps1:Expand-Archive -Force c:\ALEX\Install.zip\Fubar.zip -dest c:\TEST\

    Batch File Command that calls the Unzip.ps1 script: Powershell.exe -executionpolicy remotesigned -File %~dp0UNZIP.ps1

    Please keep in mind, I'm just learning scripting and I'm teaching myself. My knowledge is limited, but I've made it this far and this is the only part I'm stuck on. Please give clear responses. Any help or advice would be extremely appreciated! Using PowerShell 5.0

    Thanks in advance!

  • Mathias R. Jessen
    Mathias R. Jessen about 8 years
    Join-Path will take care of the \ for you
  • mklement0
    mklement0 about 8 years
    @MathiasR.Jessen: Thanks - Join-Path is definitely good to know about, but overkill in this case (the path separator is fixed, and there are no wildcards to resolve).
  • mklement0
    mklement0 about 8 years
    Some good information, but the batch file does not change the current directory: it invokes powershell.exe from whatever the current directory happens to be, using an absolute path to the PowerShell script. Aside from that: while it may be helpful to beginners to learn about Get-Location in general, its use here is unnecessary.
  • mklement0
    mklement0 about 8 years
    It may be worth mentioning that $PSScriptRoot and $PSCommandPath are v3+ and preferred there; on v2-, $MyInvocation.MyCommand.Path is preferable to $MyInvocation.InvocationName, because the latter (a) contains just . if the script is being dot-sourced, and (b) otherwise contains the invocation path as specified, which is not necessarily a full path.
  • Bacon Bits
    Bacon Bits about 8 years
    It's not overkill to use Join-Path. If the script is located in the root of C:, then $PSScriptRoot will be C:\ , with a trailing backslash. If the script is located in a subfolder named test, then $PSScriptRoot will be C:\test with no trailing backslash. Join-Path is the easiest way to deal with that.
  • mklement0
    mklement0 about 8 years
    @BaconBits: Yes, Join-Path handles root paths properly, which is preferable when the path is displayed somewhere, but functionally it makes no difference here (try Get-Item C:\\Windows), so in this case - not generally, not always - I vote for simplicity.
  • mklement0
    mklement0 about 8 years
    Good tip, but why do you think (Get-Variable MyInvocation -Scope 0).Value should be used instead of plain $MyInvocation?
  • Admin
    Admin about 8 years
    This is perfect! Thank you all for your expertise and patience! Its greatly appreciated!
  • VertigoRay
    VertigoRay about 8 years
    @mklement0 it was all just teaching points, bud. You're right, $MyInvocation.MyCommand.Path is better ... it was late when I wrote this. ;) We could argue the what ifs all day, but I was just trying give him lots of options since I don't have all of the source files. Also, why is he calling multiple .ps1 files? Why call .ps1 from .bat at all; just start in .ps1? Lot's of questions I could pose, but all beyond the scope of the OP.
  • mklement0
    mklement0 about 8 years
    I think it's great that you wanted to teach, and it is clearly what the OP wanted. Thanks for updating to $MyInvocation.MyCommand.Path. My other comments weren't about what ifs; they pointed out a false premise (that the batch file changes the current dir.), and suggested that the use of Get-Location as part of the input path to Export-Archive may be a distraction (in that it could lead a newcomer to think that it's required or good practice, which is something I didn't explicitly state in my previous comment).
  • mklement0
    mklement0 about 8 years
    One more thought re teaching: when offering multiple ways to do something, it's helpful to beginners to provide guidance as to when to choose what method; hence my suggestion to recommend use of $PSCommandPath, if available, and to mention the v3+ requirement. Come to think of it: $PSScriptRoot (v3+ also) directly provides the path to the script's folder.