811

I have seen -- used in the compgen command.

For example:

compgen -W "foo bar baz" -- b

What is the meaning of the -- in there?

ilkkachu
  • 138,973
dogbane
  • 29,677

5 Answers5

1001

More precisely, a double dash (--) is used in most Bash built-in commands and many other commands to signify the end of command options, after which only positional arguments are accepted.

Example use: Let's say you want to grep a file for the string -v. Normally -v will be considered the option to reverse the matching meaning (only show lines that do not match), but with -- you can grep for the string -v like this:

grep -- -v file
Guss
  • 12,628
  • 5
    Most notably in the Bash set built-in, where it's absolutely necessary. – l0b0 Oct 18 '12 at 09:03
  • 23
    -- works to separate options from regular expressions in grep, but the canonical way is to use -e/--regexp. – l0b0 Oct 18 '12 at 09:05
  • 10
    @l0b0: the pattern to search is normally one of the positional parameters, so it can fit after the --, though you are correct in noting that my example above could also be written as grep -e -v file (although that is very confusing). – Guss Jan 21 '15 at 16:12
  • 5
    Not all bash builtin commands accept -- as the end of option marker. [ and echo don't for instance (one of the reasons echo can't be used reliably). – Stéphane Chazelas Mar 21 '16 at 10:06
  • 1
    Should probably be "positional arguments" instead of "positional parameters". From Parameter vs Arguments: "parameter is part of the function/procedure/method signature and an argument is the actual value supplied" – David Tonhofer Feb 26 '21 at 11:04
  • @david-tonhofer - accepted though this answer is already scoring way too high for the actual value of provides... – Guss Feb 26 '21 at 14:23
  • Do the commands need to implement double dash themselves or is it a bash thing? – Ziyuan Nov 16 '22 at 13:26
  • 1
    @Ziyuan its a convention: each command that wants to support this convention needs to implement it. Luckily, its easy with the GNU libc getopt() function that has built-in support for that: https://www.gnu.org/software/libc/manual/html_node/Using-Getopt.html – Guss Nov 16 '22 at 14:48
  • @Guss thanks, good to know. – Ziyuan Nov 17 '22 at 12:57
86

In man bash we can read in Shell Builtin Commands section (online doc):

Unless otherwise noted, each builtin command documented in this section as accepting options preceded by - accepts -- to signify the end of the options.

The :, true, false, and test builtins do not accept options and do not treat -- specially. The exit, logout, break, continue, let, and shift builtins accept and process arguments beginning with - without requiring --. Other builtins that accept arguments but are not specified as accepting options interpret arguments beginning with - as invalid options and require -- to prevent this interpretation.

Note that echo does not interpret -- to mean the end of options.

kenorb
  • 20,988
59

This marks end of parameter (option) list.

Kusalananda
  • 333,661
polemon
  • 11,431
41

POSIX.1-2017

12.2 Utility Syntax Guidelines

Guideline 10:

The first -- argument that is not an option-argument should be accepted as a delimiter indicating the end of options. Any following arguments should be treated as operands, even if they begin with the '-' character.

http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02

John Doe
  • 511
33

In commands that use the POSIX getopt() API to parse options, as well as several others, -- marks the end of options.

In:

rm -f -- -f

The first -f is taken as an option to rm, while the second is taken as the name of a file to remove as everything past -- is not taken as an option.

In POSIX-compliant utilities and getopt() implementations, options are not accepted after non-option arguments, so for instance:

grep pattern -f

is required to look for lines matching pattern in the file called -f.

However, the GNU implementation of getopt() and its getopt_long() extension as used by most GNU utilities including GNU grep (but not bash nor any of its builtins even though bash is the GNU shell) doesn't do that by default unless there's a POSIXLY_CORRECT variable in the environment.

So, for those, -- is needed even if there are leading non-option arguments:

grep -- pattern -f

An older way to mark end of options was with -. You'll find that most sh implementations still support - as an end-of-option marker, as well as most of the builtins of ksh or zsh. That is however not common these days.

Some commands are known not to support -- as the end of option marker. Most echo implementations are among those. That's one of the reasons they can't be used to output arbitrary data.

⚠️ Important

That -- is needed when non-option arguments start with - (or possibly + for some commands), but also when you can't guarantee some non-option arguments won't start with -/+. That includes:

  • rm -f -- "$file"
  • rm -f -- "${files[@]}"
  • rm -f -- *.txt
  • rm -f -- $(command-that-generates-a-list-of-files-or-file-patterns)
  • cmd | xargs rm -f --

If you can't guarantee the command supports -- as the end of option marker, for those arguments that are file names, another approach is to prefix the file name with ./:

somegrep pattern ./*.txt

Failing to add those -- can introduce arbitrary command injection vulerabilities, especially if you also forget to quote your expansions. For instance:

sed -e 's/foo/bar/' *.txt

Will run reboot if there's a file called '-es/.*/reboot/e;#.txt' in the current working directory. Fixed by changing it to sed -e 's/foo/bar/' -- *.txt or sed -e 's/foo/bar/' ./*.txt.

Same for:

rename 's/foo/bar/' *foo*

with some rename variants; beware some (non-vulnerable) rename variants don't support --.

print $var

In zsh (even though leaving parameter expansions unquoted is relatively safe in that shell), will run reboot if $var contains -va[1$(reboot)]. Fixed with print -- $var or print - $var though you likely want print -r -- $var to also disable the backslash processing.

  • 1
    Could you link to the posix getopt() API? Else, really great answer, take my upvote! – Junaga Mar 23 '21 at 14:00
  • 1
    @Junaga, I've added a link to the HTML version (so non-official) of the getopt() specification in the 2018 edition. The PDF one (official) is not publicly available, though you can register (for free) with the opengroup to obtain it for free. – Stéphane Chazelas Mar 23 '21 at 14:04
  • thank you for your 'Important' remark. It answered my issue. – Ajo Jun 25 '22 at 15:56