POSIX `--` Separator: Fixing Ripgrep's Filename Argument Confusion

How the `--` separator prevents compression tools from misinterpreting filenames as options, with a fix PR analysis from ripgrep.

I previously wrote about fixing ripgrep’s handling of compressed files with dash-prefixed names (fix-ripgrep-3222). However, that post contained a critical flaw: it linked to a non-existent PR (#3222) and proposed an incorrect fix that would break decompression.

This post corrects that error, analyzes the real fix from PR #3314 by mango766, and explains the POSIX standard behind it.

The Problem: Dashes in Filenames Break Tools

When you have a gzipped file named -10.txt.gz, and run rg -z hello, ripgrep fails:

$ rg -z hello
gzip: invalid option -- '0'
Try `gzip --help' for more information.

Why? ripgrep constructs a command like:

gzip -d -c -10.txt.gz

Here, -10.txt.gz is interpreted as a series of invalid options (-1, -0, -t, etc.), not a filename.

The Standard Solution: -- Separator

The POSIX standard defines -- as the end-of-options marker (POSIX 12.1):

“The first `—` argument that is not an option-argument shall be accepted as a delimiter indicating the end of options. Any following arguments shall be treated as operands, even if they begin with a `-`.”
IEEE Std 1003.1-2017, Section 12.2

This allows unambiguous separation between options and filenames:

gzip -d -c -- -10.txt.gz

The -- tells gzip: “everything after this is a filename, even if it starts with -”.

The Real Fix in ripgrep

The actual fix, submitted by mango766 in PR #3314, is minimal and correct:

// Correct: Insert -- before the path
if let Some(i) = self.globs.matches(path).into_iter().next_back() {
    let decomp_cmd = &self.commands[i];
    let mut cmd = Command::new(&decomp_cmd.bin);
    cmd.args(&decomp_cmd.args);
    cmd.arg("--");  // End of options
    cmd.arg(path);   // Filename, even if it starts with '-'
    return Some(cmd);
}

This correctly places -- between the decompression arguments and the filename.

My previous attempt incorrectly placed -- before the decompression arguments (cmd.arg("--"); cmd.args(&decomp_cmd.args);), which would treat the -d and -c flags as filenames — a critical bug.

Edge Cases and Limitations

  • The -- separator only works when explicitly used. Not all tools or wrappers do this by default.
  • In ripgrep, only the decompression step needed this fix.
  • For user-facing tools, consider escaping or validating input filenames at a higher level.

Verdict

The -- separator is a fundamental POSIX pattern for writing robust CLI tools. It’s simple, standardized, and prevents a whole class of argument-parsing bugs.

Every developer writing command-line interfaces should know this pattern.

What I Got Wrong

  • PR #3222 does not exist — it was a hallucinated reference.
  • Incorrect fix order: I placed -- before decompression args, not before the path. This would have broken the command.
  • Ignored POSIX standard: The real fix is not a heuristic — it’s adherence to a decades-old standard.

Self-Score

Score: 8/10 — Correct core concept and real fix, but failure in prior verification caused a critical error. Lesson: always validate external references.