1

I have a Powershell script that runs fine in VSCode but from the Powershell Prompt, I'm getting an error. Below is the output.

→ C:\WINDOWS\system32› powershell.exe -file 'D:\Source\Repos\Powershell Scripts\SD-Report-Archive.ps1' -sourcePath 'D:\Archives\' -targetPath 'D:\Archives2\'
D:\Archives\
Get-ChildItem : Cannot find path 'D:\A' because it does not exist.
At D:\Source\Repos\Powershell Scripts\SD-Report-Archive.ps1:25 char:14
+     $files = Get-ChildItem -Recurse -File -Path $sourcePath
+              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (D:\A:String) [Get-ChildItem], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

As you can see on the 2nd line of the output I do a Write-Output of the parameter value that I am sending in and it's correct. When I execute Get-ChildItem it seems to truncate the value to 'D:\A' and I don't know why.

Param(
    [Parameter(Mandatory = $true)]
    [string]$sourcePath,
    [Parameter(Mandatory = $true)]
    [string]$targetPath
)

function Copy-FilesIntoFolders {
    param()

    Write-Output $sourcePath;

    $files = Get-ChildItem -Path $sourcePath -Recurse -File
...
}
Tim
  • 1,249
  • 5
  • 28
  • 54
  • 2
    Do you have the same issue if you remove the trailing backslash from your paths? – TheMadTechnician Oct 18 '19 at 00:06
  • @TheMadTechnician I do not. Thanks so much. I never would have thought that would have been the problem. – Tim Oct 18 '19 at 00:59
  • I just found this post that seems to be related for anyone visiting this post in the future. It may be the reason this behavior was happening. https://stackoverflow.com/questions/5079413/how-to-pass-boolean-values-to-a-powershell-script-from-a-command-prompt. The key statement to take in is "it appears that powershell.exe does not fully evaluate script arguments when the -File parameter is used." – Tim Oct 18 '19 at 01:17
  • It works for me from what is shown. Maybe it's better to show the whole script. – js2010 Oct 18 '19 at 04:02
  • 1
    @Tim, the linked post is unrelated (it's a separate bug); your problem is solely related to PowerShell's broken behind-the-scenes re-quoting. – mklement0 Oct 18 '19 at 05:25
  • @mklement0 thanks for the update. Just getting back into the PowerShell scene a little more. I'm amazed at what it grown into and can do. – Tim Oct 18 '19 at 19:09

2 Answers2

1

On Windows, PowerShell - of necessity - rebuilds the command line in order to invoke external programs.

Notably, most external programs don't understand single-quoted strings ('...') via their CLI, so after having performed its own parsing, PowerShell re-quotes the resulting (stringified) arguments using double quotes ("...") if it deems that necessary.

Unfortunately, this re-quoting is broken in several respects:

  • If the argument value doesn't contain spaces, no quoting is applied. Values without spaces but with special characters may therefore break commands, especially when another shell, such as cmd.exe is invoked.

    • E.g., cmd /c echo 'a&b' breaks, because a&b is ultimately passed without quotes, and & has special meaning in cmd.exe
  • If the argument has embedded double quotes (" chars.), the re-quoting does not automatically escape them for syntactically correct embedding inside "..." or unquoted literal use:

    • E.g., foo.exe 'Nat "King" Cole' is translated to foo.exe "Nat "King" Cole" - note the lack of escaping of the inner " chars. - which results in a different string when parsed by most applications, namely Nat King Cole (no double quotes).

    • You have to perform escaping manually, in addition to PowerShell's own escaping requirements, if applicable: foo.exe 'Nat \"King\" Cole' or, with double-quoting,
      foo.exe "Nat \`"King\`" Cole" (sic).

  • Similarly - as in your case - if the argument has spaces and ends in \, that trailing \ is not escaped in the resulting double-quoted string, which breaks the argument syntax:

    • E.g., foo.exe 'a b\' c becomes foo.exe "a b\" c - however, most programs - including PowerShell's own CLI - interpret the \" as an escaped " char. rather than the closing double quote, resulting in misinterpretation of the argument, merging it with the next argument to result in
      a b" c

    • Again you have to perform escaping manually, by doubling the \:
      foo.exe 'a b\\' c

      • Alternatively, if the argument happens to be a directory path whose trailing \ is optional, simply omit the latter.
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • So I guess this is why we have to jump through hoops to send in -Command arguments via a scheduled Task I found I had to add "& then double single quote all string values, then close out the double quote at the end. – Tim Oct 18 '19 at 19:16
  • @Tim: If you're calling the PowerShell CLI (`powershell.exe`) from _outside_ of PowerShell, then different rules apply. With `-File`, you'll have to use _double_-quoted strings, but the requirement to double the trailing `\ ` still applies. Yes, passing a single piece of PowerShell code to `-Command` is a viable workaround, though there are differences in behavior, notably with respect to how the exit code is determined - see https://stackoverflow.com/a/57443822/45375. – mklement0 Oct 18 '19 at 19:49
  • thanks for the reference. That post explains a lot. – Tim Oct 19 '19 at 03:43
0

Get-ChildItem : Cannot find path 'D:\A' because it does not exist.

The backslash (\) here looks like an escape character to me. Please try with \\

goooooooo
  • 90
  • 1
  • 5
  • The error message stems from a PowerShell cmdlet. In PowerShell code `\ ` does _not_ function as an escape character (the backtick (`\``) serves that function). – mklement0 Oct 18 '19 at 05:27