Search code examples
pythonpowershellcommand-line

Powershell and CMD combining command-line filepath arguments to Python


I was making user-entered variable configurable via command line parameters & ran into this weird behaviour:

PS D:> python -c "import sys; print(sys.argv)" -imgs ".\Test V4\Rilsa\" -nl 34
['-c', '-imgs', '.\\Test V4\\Rilsa" -nl 34']

PS D:> python -c "import sys; print(sys.argv)" -imgs ".\TestV4\Rilsa\" -nl 34
['-c', '-imgs', '.\\TestV4\\Rilsa\\', '-nl', '34']

If the name of my folder is Test V4 with a space character, then all following parameters end up in the same argument element '.\\Test V4\\Rilsa" -nl 34'. There is also a trailing " quote after the directory name. I tried this again in CMD, thinking it was a Powershell quirk & experienced the same behaviour.

What's going on here? I'm assuming it has something to do with backslashes in Powershell -- though it's the default in Windows for directory paths -- but why do I get diverging behaviour depending on space characters & what's a good way to handle this assuming Windows paths are auto-completed into this form by the shell (i.e. trailing \)?


Solution

    • You're seeing a bug in Windows PowerShell (the legacy, ships-with-Windows, Windows-only edition of PowerShell whose latest and last version is 5.1), which has since been fixed in PowerShell (Core) 7, as detailed in this answer.

    • In short, as you've since discovered yourself, the problem occurs when you pass arguments that contain space(s) and end in \ to external programs, because Windows PowerShell - when it constructs the true process command line behind the scenes - blindly encloses such arguments in "...", causing most target programs to interpret the closing \" sequence as an escaped " char.
      (Since arguments without spaces are not subject to this "..." enclosure, they are not affected.)


    Workarounds (required in Windows PowerShell only, but should also work in PowerShell 7):

    • Manually add a trailing \ to your argument:

      # Note the '\\'
      python -c "import sys; print(sys.argv)" -imgs ".\Test V4\Rilsa\\" -nl 34
      
    • Alternatively, add a trailing space, relying on the fact that on Windows trailing spaces in paths are usually ignored:

      # Note the trailing space before the closing "
      python -c "import sys; print(sys.argv)" -imgs ".\Test V4\Rilsa " -nl 34
      

    In either case, if the path must be passed via a variable rather than a literal and the variable value may or may not end in \, use "...", i.e. an expandable (interpolating) string, such as "$dirPath\" or "$dirPath "