2

I have a file called -. I want to display its contents.

  • One way is to do
    cat ./-
    
    since cat - reads from standard input.
  • However, why are cat "-" and cat '-' also interpreted by the shell as cat -?
AdminBee
  • 22,803
Jhdoe
  • 123
  • 4
    cat is specifically implemented to interpret the file name - as "use stdin". This means you cannot quote - to remove this meaning. If you pass additional quotes to cat, e.g. with cat \"-\", it will look for a file name "-" that includes quotes. So cat would need an option to disable the special meaning of -. If you happen to have a file - and pass it to cat by shell globbing, e.g. cat *, you would have to use something like cat ./* to work around the problem. For better suggestions, please, describe your use case where you have to avoid problems with a file -. – Bodo Feb 12 '19 at 11:41
  • 1
    Related: https://unix.stackexchange.com/q/1519/117549 – Jeff Schaller Feb 12 '19 at 13:52
  • 4
    Note that the purpose of ./- is not to "disguise" the argument from the shell, but to make it distinct from - for cat. The convention that - represents standard input is handled internally by programs. – chepner Feb 12 '19 at 14:23
  • The answer is in the manual: https://www.gnu.org/software/bash/manual/bash.html#Quote-Removal – glenn jackman Feb 12 '19 at 14:59
  • There are no "standards" for command line arguments/options. The shell only has the job to divide what you wrote into substrings, then everything is passed to the command you chose and all the interpretation is done by said command. The shell only does command substitution, variable substitution, brace expansion, wildcard expansion and quoting expansion in order to get the final command line (not in this order). Quoting is only useful to tell the shell how to split the string into arguments but it does not change the values of the arguments passed. – Giacomo Alzetta Feb 12 '19 at 15:19
  • 1
    @GiacomoAlzetta, Of course there are, and many to choose from. But seriously, there are guidelines (POSIX Utility Syntax Guidelines), and conventions (getopt), if not all-encompassing mandatory standards. – ilkkachu Feb 12 '19 at 17:44
  • Related: https://unix.stackexchange.com/a/16364/50240 – bishop Feb 12 '19 at 20:32
  • This question has been asked before. Beware however that the currently accepted answer is wrong: it's completely irrelevant that cd is a builtin, and exactly the same phenomenon happens with cat or any other command. – Gilles 'SO- stop being evil' Feb 12 '19 at 21:00

3 Answers3

16

The shell removes any quotes before cat sees them. So cat - and cat "-" and cat '-' all get passed through as the array ['cat', '-'] after whitespace tokenization, wildcard expansion, and quote removal by the shell.

tripleee
  • 7,699
6

Quotes are use by the shell, and not passed to the command:

e.g.

cat "hello world" #this passes the single token `hello world`, to `cat`, as argument 1. However the quotes are not passed.
cat "-" # this passes the single token `-`, to `cat`, as argument 1. The same as if no quotes had been used.
0

GNU cat is coded to compare the given filename against the string "-" before trying to open it as a file:

  if (STREQ (infile, "-"))
    {
      have_read_stdin = true;
      input_desc = STDIN_FILENO;
      if (file_open_mode & O_BINARY)
        xset_binary_mode (STDIN_FILENO, O_BINARY);
    }
  else
    {
      input_desc = open (infile, file_open_mode);
      if (input_desc < 0)
        {
          error (0, errno, "%s", quotef (infile));
          ok = false;
          continue;
        }
    }

So, if you have a file named -, you need to defeat this logic by giving cat the path and name.

Quotation marks protect a value from white space splitting and, if single quotes, variable expansion. Quotation marks do not signal that the thing quoted is a file. To explicitly signal a value is a file, prefix it with a relative or absolute path.

All that said, one might suggest that GNU cat should also check if - is a file in the current working directory, but it'd be unusual for filenames to start with a hyphen or to be solely a hyphen, so that may be a performance concern. David Wheeler's essay, Fixing UNIX and Linux Filenames, has a nice discussion of this problem in historical context.

bishop
  • 3,209
  • 4
    This behaviour (- designating standard input) is mandated by the POSIX standard for the cat utility and therefore not something that GNU cat should or could change. – Kusalananda Feb 12 '19 at 19:55
  • @Kusalananda GNU could change it: "The GNU Project regards standards published by other organizations as suggestions, not orders. We consider those standards, but we do not 'obey' them." With POSIXLY_CORRECT unset, GNU cat could also check for a file in the current working directory named - and use that file. That is likely the desired behavior, even though it countermands the letter of the standard. – bishop Feb 12 '19 at 20:42
  • 2
    It would also possibly break existing code and/or introduce serious security issues (add a file called - and have it be injected in place of standard input in a pipeline). – Kusalananda Feb 12 '19 at 20:46