5

If I have a file which name starting with single or several minus sign, for example --1 it can't be used as a parameter of many commands. Even if I run

cat --1

instead of file content I get unrecognised option error message:

cat: unrecognized option '--1'

Same effect appears when I type

cat "--1"
cat '--1'
cat \-\-1

nothing works. Files starting with '-' is not illegal in file system, but it is quite hard to work with them in cli and scripts. OK, in case of cat or grep I can use

cat <--1

and this will work, but

rm --1

won't work either and it is quite hard to substitute this command with something else. Very uncomfortable after all. Is there any universal workaround different from not using such file names?

BTW, if name file is single - and all most of commands will understand it as stdin, wouldn't they?

And it would be hard not to use such file names since I can't rename them all in script using mv command either.

Nick
  • 269

4 Answers4

18

The convention is that everything that starts with a - is an option. This collides with filenames that start with -. To work around this most commands recognize -- as an end-of-options sentinel. Everything after the -- will not be recognized as options, instead will be taken literally as filenames.

For example

cat -- --1

or

rm -rf -- --1

Another method is to qualify the filename with a path.

for example

cat ./--1

or even

cat /home/username/foo/--1

Best is to never create filenames that start with -. Filenames may contain all kinds of characters, including newline, but only because it is possbile doesn't mean you have to do it.

To answer your question: Why cat, grep and other commands can't understand files starting with minus sign? Because the command line is just a string. The shell does some filename expansion and some word splitting and some variable replacement but in the end the program receives an array of strings.

How do you separate the strings which mean options from the strings which mean filenames? By some sentinel characters. The convention in the unix/linux world is to use the - as the sentinel character. Everything that starts with a - is an option. Well, almost everything. Arguments to options might also start with -, but that is detail of the individual program.

Not every program conforms to this convention. Arguably the most popular example is dd:

dd if=/dev/random of=whatfreespace

dd recognizes filenames because they appear after the = sign.

Most, if not all, GNU programs conform to the GNU getopt convention: short options start with - and are only one character, long options start with -- and are at least two characters. -- on its own marks the end of options and all strings after that mark are not recognized as options. For more details read Program Argument Syntax Conventions in the GNU libc manual.

One example of a program that only partially conforms to the GNU convention is find:

find -not -name porn -delete

While find uses - to differ options from non options, it does not differ long options from short options. It does recognize -- as end of options. Every program can define it's own way how to recognize options and filenames and whatever else.

The convention to interpret a single - as stdin is also just that, a convention of how to interpret the single - character when it is encountered in the array of strings.

Lesmana
  • 27,439
  • find has short (-H, -L...) and long options (for GNU find: --help, --version) and predicates (-print, !, -type, (...). -- marks the end of options in find as well (but doesn't prevent a filename from being taken as a predicate, for which the only option is to prefix relative paths with ./). – Stéphane Chazelas Aug 19 '13 at 20:48
  • Note that the issue is worse with GNU than with Unix tools because GNU getopt accepts options even after non-option arguments (unless $POSIXLY_CORRECT is set). – Stéphane Chazelas Aug 19 '13 at 20:50
5

Anything starting with a dash is considered an option. You can prepend path to the filename to make it not look like an option:

cat ./--filename
choroba
  • 47,233
  • Haha, you beat me to it. It's a good habit to prepend ./ to any globs, to (so ./* rather than *), though of course there are a few cases where that isn't helpful (using bash's built-in string manipulation to rename the beginning of files, for example), which is where the -- answers help. – evilsoup Aug 19 '13 at 16:54
  • Hm, I always thought than *nix shell requirement to type ./ before name of executable to run files placed in current directory is very very stupid... but looks like it has deeper meaning and ./ might be quite good habit. – Nick Aug 19 '13 at 17:05
  • 1
    @Nick Using ./ to run executables in the current directory has a different purpose. In the days of massively multiuser time-sharing systems, it was dangerous to put . in $PATH, because then you might forget which directory you were in and run someone else's a.out, and perhaps someone else felt like leaving a program that did rm -rf $HOME around to trip people up, for the lulz. Or worse. Nowadays that risk is largely of the past. – zwol Aug 19 '13 at 18:25
3

Using cat -- --filename indicates to stop looking for any option. There is a potential problems with this approach since third party applications do not know how to handle dashed file names. However POSIX binaries like, cp, rm, cat etc know how to handle these dashes

1

More generally, it's:

cat $options_and_or_file_patterns_or_stdin_if_dash

cat "$option_or_file_or_stdin_if_dash"

cat -- "$file_or_stdin_if_dash"

cat < "$file"

cat - < "$file" # same as above