powershellbatch-filepathliteralshybrid

How do I get PowerShell script path of and inside Batch-Powershell Hybrid script even when the path contains multiple occurrences of Square Brackets?


See the below script that I have for renaming Movie files with .mp4 or .mkv extensions:

<# ::

@echo off
powershell -c "iex ((Get-Content -LiteralPath '%~f0') -join [Environment]::Newline); iex 'main %*'"
pause && goto :eof
#>

$path = Split-Path -Path $PSScriptRoot -Parent
echo $path
## $validExtensions = '.mkv', '.mp4'
## Get-ChildItem -LiteralPath $path -File |
##     Where-Object Extension -In $validExtensions |
##     Where-Object Length -GT 500mb |
##     Rename-Item -NewName {
##         ($_.BaseName -replace 'MkvHub.com - ' -replace '(?<=1080p|720p).+' -replace '\.', ' ').ToUpper() + $_.Extension
##     }

Currently I have commented out the rest of the Powershell code for obvious reason of testing whether the code Split-Path -Path $PSScriptRoot -Parent or split-path $myInvocation.myCommand.path would give us the literal path of this same script during execution or not.

Clearly it doesn't give me the expected path, and instead throws this error:

Split-Path : Cannot bind argument to parameter 'Path' because it is an empty string.
At line:7 char:26
+ $path = Split-Path -Path $PSScriptRoot -Parent
+                          ~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Split-Path], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Microsoft.PowerShell.Commands.Spli
   tPathCommand

Press any key to continue . . .

Also the similar error with split-path $myInvocation.myCommand.path too.

As aforementioned I already tried both split-path $myInvocation.myCommand.path and Split-Path -Path $PSScriptRoot -Parent, but none of them are working.

Can anyone help figuring this out ? I have to make this work in hybrid script only...


Solution

  • Fundamentally, the automatic $PSScriptRoot and $PSCommandPath are only defined inside script files (*.ps1) (and script module files (*.psm1)).

    Since your hybrid batch-file approach relies on invoking powershell.exe, the Windows PowerShell CLI, by directly passing source code to its -c (-Command) parameter, these variables are therefore not defined.

    As Mofi suggests, you can set a variable with the batch file's own directory before calling powershell.exe, which - due to cmd.exe variables also being environment variables - the PowerShell code can then access as such, using the $env: namespace.

    However, a robust hybrid batch/PowerShell solution requires the use of the -File PowerShell CLI parameter in order to correctly handle pass-through arguments, which in turn requires creating a temporary copy of the batch file as a .ps1 file.

    See this answer, which combines the -File approach with passing the original batch-file path via an environment variable, allowing the PowerShell code to infer the equivalents of $PSScriptRoot and $PSCommandPath.