Search code examples
node.jswindowspowershellyargs

Argument escaping not interpreted correctly when running node.js script from Windows PowerShell


Given the following script:

const yargs = require('yargs');

const argv =
    yargs
        .usage('Usage: $0 [--whatIf]')
        .alias('d', 'directory')
        .alias('wi', 'whatIf')
        .nargs('d', 1)
        .describe('d', 'alphabetize this directory')
        .describe('whatIf', 'show what would happen if run')
        .demandOption(['d'])
        .argv;

console.log(argv.directory);

If I invoke the script from Windows PowerShell like so: node .\alphabetizer.js -d 'l:\my folder\Files - Some Files In Here\' --whatIf I get the output l:\my folder\Files - Some Files In Here\" --whatIf where I would expect just l:\my folder\Files - Some Files In Here\. It works OK with folder names that require no escaping, but it seems to get confused by the escaping.

If I examine process.argv, I can see the same escaping issue.

I have noticed that if I remove the trailing slash it will work. However, this still points to the node script not handling the input properly, because this should not be necessary with string set off by single quotes.

Is there a way to make this work?


Solution

  • Both Windows PowerShell (powershell.exe) and PowerShell (Core) (pwsh) up to v7.2.x are fundamentally broken with respect to quoting arguments for external programs properly - see this answer for background info.

    Generally, PowerShell on Windows has to perform re-quoting behind the scenes in order to ensure that just "..."-quoting is used, given that external programs can't be assumed to understand '...'-quoting too when parsing their command line (which on Windows every program has to do itself).

    Windows PowerShell is more broken with respect to arguments that end in \ and have embedded spaces, re-quoting them improperly; e.g.:

    PS> foo.exe 'c:\foo \' bar
    

    is translated into the following command line behind the scenes:

    foo.exe "c:\ foo \" bar
    

    This is broken, in that most applications - including PowerShell's own CLI - sensibly assume that the \" is an escaped " char. to be taken verbatim, thinking that the argument continues with  bar and then implicitly ends, despite the formal lack of a closing ".

    PowerShell (Core) v6+ more sensibly translates the above to foo.exe "c:\foo \\" bar, where the \\ is interpreted as an escaped \, and the following " again has syntactic function.


    If you're stuck with Windows PowerShell, your only choices are:

    • either: if possible, leave off the trailing \
    • otherwise: manually double it (\\), but only do so if the argument also contains spaces (otherwise, the \\ will be retained as-is, though in the case of filesystem paths that is usually benign).