Skip to content

Consider dropping custom command line parsing to use “standard” rules #7

@ymeine

Description

@ymeine

While using the Everything CLI from both PowerShell and Node.js, I noticed a strange behavior: when starting to include spaces in some paths prefixed with a function, things would break.

Example: es folder:"folder/with some/spaces".

Turns out, by default, PowerShell or Node.js would quote and escape the argument to make sure it’s received as a single one, as long as the target application uses “standard” command line parsing to grab the arguments. This is not the case in Everything CLI.

So it receives "folder:\"folder/with some/spaces\"" (I omitted the beginning which contains the executable path). In the source code, _es_get_argv and _es_get_command_argv will not take into account the \" escape sequences and therefore count the second quote as a closing quote, ending up with two, weird arguments instead of one:

  • "folder:\"folder/with
  • some/spaces\""

I did some tests, changing the source code, and using argv from C instead seems to be working perfectly fine, at least for my simple use cases. CommandLineToArgv would probably work equally.

So the big question is: besides unfortunate backwards-compability requirements, what are the reasons Everything CLI is doing custom parsing? Could it be possible to use standard runtime behavior instead, in a future major version for instance? Or maybe an alternate build?

Context

  • Everything version: 1.5.0.1404a
  • Everything CLI version: 1.1.0.36

Reproduction

Example given for PowerShell, in a temporary, isolated directory:

mkdir 'folder/with some/spaces' # create a hierarchy with some spaces
es folder:"folder/with some/spaces" # search for it and see it break

There may be other use cases where it breaks, I haven’t taken the time to investigate more.

Workarounds

For those who are facing the same issue as me.

The list is not exhaustive, and solutions should be adapted to your context.

PowerShell

⚠️ It looks like this solution only works inside scripts, not in interactive shells. I haven’t pushed investigations further.

function es {
	try {
	    $previous = $PSNativeCommandArgumentPassing
	    $PSNativeCommandArgumentPassing = 'Legacy'
	    $output = es.exe @args # don't forget the extension to avoid infinite recursion
	} finally {
	    $PSNativeCommandArgumentPassing = $previous
	}
	$output
}
# instead of: 
# es @args

Node.js:

import process from 'node:process';
import {spawn} from 'node:child_process';

const exe = 'es';
const args = process.argv.slice(2);

const proc = spawn(exe, args, {stdio: 'inherit', windowsVerbatimArguments: true});
// instead of: 
// const proc = spawn(exe, args, {stdio: 'inherit'});

Python

import shutil
import sys
from subprocess import Popen

exe = shutil.which('es')
args = sys.argv[1:]

proc = Popen(' '.join([exe] + args))
# instead of: 
# proc = Popen([exe] + args)

proc.wait()

My workaround

What I actually did on my side is to use the Python wrapper (slightly improved compared to the snippet above), and bundle it with PyInstaller, then publish it as a release asset in my fork. That was also the perfect opportunity to use a more “standard” naming scheme for the asset, so that tools like mise-en-place would be able to find it more easily.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions