I have a file called -
. I want to display its contents.
- One way is to do
sincecat ./-
cat -
reads from standard input. - However, why are
cat "-"
andcat '-'
also interpreted by the shell ascat -
?
I have a file called -
. I want to display its contents.
cat ./-
since cat -
reads from standard input.cat "-"
and cat '-'
also interpreted by the shell as cat -
?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.
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.
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.
-
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
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
-
and have it be injected in place of standard input in a pipeline).
– Kusalananda
Feb 12 '19 at 20:46
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 tocat
, e.g. withcat \"-\"
, it will look for a file name"-"
that includes quotes. Socat
would need an option to disable the special meaning of-
. If you happen to have a file-
and pass it tocat
by shell globbing, e.g.cat *
, you would have to use something likecat ./*
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./-
is not to "disguise" the argument from the shell, but to make it distinct from-
forcat
. The convention that-
represents standard input is handled internally by programs. – chepner Feb 12 '19 at 14:23cd
is a builtin, and exactly the same phenomenon happens withcat
or any other command. – Gilles 'SO- stop being evil' Feb 12 '19 at 21:00