In commands that use the POSIX getopt() API to parse options, as well as several others, -- marks the end of options. In: rm -f -- -fThe 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 -fis 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 -fAn 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. ⚠️ ImportantThat -- 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 ./*.txtFailing 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/' *.txtWill 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 $varIn 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. (责任编辑:) |