Search code examples
powershellpath

Why the same PowerShell path sometimes works and sometimes does not?


I am new to PowerShell (coming from Bash), and I cannot understand why a path I build sometimes works and others does not:

PS > E:\Installers\wget\wget-win32-1.19.1.exe --version
GNU Wget 1.19.1 built on mingw32.
...OK

PS > $rootPath = E:\Installers
PS > echo ${rootPath}
E:\Installers
PS > dir ${rootPath}\wget\wget-win32-1.19.1.exe


    Directory: E:\Installers\wget


Mode                 LastWriteTime         Length Name                                                                                                                                        
----                 -------------         ------ ----                                                                                                                                        
-a----        19/04/2024     15:50        3481920 wget-win32-1.19.1.exe

PS > ${rootPath}\wget\wget-win32-1.19.1.exe --version
At line:1 char:12
+ ${rootPath}\wget\wget-win32-1.19.1.exe --version
+            ~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unexpected token '\wget\wget-win32-1.19.1.exe' in expression or statement.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : UnexpectedToken

Why does ${rootPath} work with dir and not on its own?


Solution

  • Why does ${rootPath} work with dir and not on its own?

    This is explained in Argument mode from the about Parsing documentation. In particular, the bullet point:

    So, essentially, the argument passed to dir (Get-ChildItem) is interpreted as if it was an expandable string "${rootPath}\wget\wget-win32-1.19.1.exe":

    $rootPath = 'E:\Installers'
    & {param([string] $path) $path } ${rootPath}\wget\wget-win32-1.19.1.exe
    
    # Parameter binding resolves the concatenation as:
    # `E:\Installers\wget\wget-win32-1.19.1.exe`
    

    However in your second attempt PowerShell can't determine that what you're trying to do is create a new string by concatenating the variable value ${rootPath} with the rest of the string \wget\wget-win32-1.19.1.exe, you need to help it by wrapping the expression in an expandable string and then invoking that path either with dot-sourcing operator . or call operator & both detailed in about Operators:

    & "${rootPath}\wget\wget-win32-1.19.1.exe" --version
    

    Other alternative could be to use Join-Path:

    & (Join-Path ${rootPath} wget\wget-win32-1.19.1.exe) --version
    

    Or, perhaps invoking the command info instance, here PowerShell auto-resolves the path to winget for you:

    & (Get-Command winget) --version