Skip to content

Windows: MSYS children glob and unescape on-file argv #1191

Closed

Description

@thekevinbot

Found while fixing #1186 on the Windows CI job (PR #1190, run https://github.com/thekevinscott/dirsql/actions/runs/36432881839).

command::run_command spawns argv through Rust's Command::args. On Windows, Rust wraps an argument in quotes only when it contains a space or tab. An MSYS/Cygwin child (Git for Windows' sh, printf, echo, cat, ...) re-parses its command line and runs glob and brace expansion on every unquoted word, with \ as the escape character. That breaks the contract in command.rs ("no globbing", "a substituted value is always exactly one argv element") when the child is an MSYS program:

  • on-file = "printf '[{}]'": printf receives [] after brace expansion, so every file yields zero rows.
  • printf '[{"n":1}]' and printf ... {abspath} are mangled the same way.
  • A {path} in verbatim form (\\?\C:\Users\..., which fs::canonicalize and a canonical cwd produce on Windows, and which the watcher reports) contains ?, so it is globbed, and its backslashes are eaten as escapes: the hook sees \?C:Usersrunneradmin...in_cwd.txt.

Tests red for this reason on Windows (after #1190):

  • internal_tests::build_tables_from_config_creates_on_file_tables, run_on_file_parses_command_json_output, run_on_file_does_not_substitute_abspath
  • async_sdk::from_config_path_loads_config_with_explicit_root
  • sdk::builder_config_loads_tables_with_explicit_root, builder_appends_programmatic_tables_to_config_tables
  • cli_repeatable_config_e2e::query_subcommand_accepts_repeated_config_flags, server_serves_tables_from_two_config_flags
  • cli_e2e::config_elsewhere_indexes_invocation_cwd_not_config_parent
  • config_root_derivation::config_without_root_indexes_process_cwd_not_config_parent (verbatim cwd)
  • cli_e2e::get_events_emits_insert_event_when_file_created, cli_integration::get_events_streams_mutation_events, and probably cli_e2e::server_indexes_a_directory_moved_into_the_root (watcher paths are verbatim)

Options, not mutually exclusive:

  1. On Windows, force-quote every argument (CommandExt::raw_arg with MS quoting). MSYS does not glob a quoted word, and MSVCRT children parse it the same as today. Caveat: inside quotes MSYS collapses \\ to \, which MS rules keep, so a \\?\ or UNC value still differs between the two runtimes.
  2. Hand hooks a non-verbatim path (C:\... instead of \\?\C:\...). This removes the ? and the doubled backslash, and helps any Windows tool that rejects verbatim paths.
  3. Declare MSYS children unsupported and rewrite the fixtures. Users on Windows would still hit this.

Activity

  1. thekevinbot commented on Oct 2, 2026

    @thekevinbot
    CollaboratorAuthor

    Resolved on main by #1209 (force-quote every argv element on Windows, MS quoting rules) and #1211 (hand hooks non-verbatim paths), options 1 and 2 above. The Windows CI job added in #1178 runs the tests listed here and is green on main at efee3e9 (run 37058064986, job "Windows tests"). Closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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