4

How do I use GNU touch to update a file called -?

How do I use GNU cat to display a file called -?


I'm running:

% cat --version | head -n1
cat (GNU coreutils) 8.29
% touch --version | head -n1
touch (GNU coreutils) 8.29

Firstly, touch:

% touch -
% ls -l
total 0
% touch -- -
% ls -l -- -
ls: cannot access '-': No such file or directory

Ok, I'll give up on creating a file with touch. Let's create it with date instead:

% date > -
% ls -l -
-rw-r--r-- 1 ravi ravi 29 Sep  8 19:54 -
%

Now, let's try to cat it:

% cat -
% # I pressed ^D
% cat -- -
% # Same again - I pressed ^D

I know I can work around with:

% > -

and

% cat < -

But why don't these GNU utils support the convention that -- means that everything following is treated as a non-option?

How do I use these tools in the general case, for example I have a variable with the contents -?

marked as duplicate by Barmar, Jeff Schaller, αғsнιη, Thomas, thrig Sep 8 '18 at 14:47

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • @Barmar This is a more specific case where the filename is -. A lone - is not an option, so the issue is different from the issues in the questions that you have proposed as duplicates. – Kusalananda Sep 8 '18 at 14:04
  • @Kusalananda A lone - is going to be treated as an invalid option or a placeholder for stdin or stdout. In any case, the solution is the same. – Barmar Sep 8 '18 at 14:05
  • @Barmar Ok, but - is not an invalid option. – Kusalananda Sep 8 '18 at 14:10
  • @Kusalananda If it's not treated as a synonym for stdin/stdout (as touch and cat do), and it's not a filename, and it's not an actual option, what else could it be? But this is immaterial, my point is that you use the same solution to deal with any filename beginning with -, it doesn't matter why it doesn't work normally. – Barmar Sep 8 '18 at 14:14
  • Forget off the top of my head, but ti was covered in this "game" that teaches you escaping stuff, etc. Really like this series they have for learning a higher level of command line use... and I've been using Linux for 20 years... overthewire.org/wargames/bandit/bandit0.html – ivanivan Sep 8 '18 at 14:45
11

Use an explicit path to the file:

touch ./-
cat ./-

GNU touch treats a file operand of - specially:

A FILE argument string of - is handled specially and causes touch to change the times of the file associated with standard output.

For cat, the POSIX standard specifies that a file operand - should be interpreted as a request to read from standard input.

The double-dash convention is still in effect, but it's not for signalling the end of arguments but the end of options. In neither of these cases would - be taken as an option (a lone - can not be an option) but as an operand ("file name argument").


Regarding your last question:

To protect the contents of a variable against being interpreted as a set of options when using it as

utility "$variable"

use

utility -- "$variable"

Note that if the utility is cat, sed, awk, paste, sort and possibly a few others (or GNU touch), and $variable is -, this will still cause the utility to do its special processing since, as said above, - is not an option. Instead, make provisions so that filenames, if they may start with or are equal to -, are preceded by a path, for example ./ for files in the current working directory.

A good habit is to use

for filename in ./*; do

rather than

for filename in *; do

for example.

  • It seems the way for absolute belts and braces would be to do cat "$(readlink -f "$file")" – Tom Hale Sep 8 '18 at 13:35
  • What about the touch case though? – Tom Hale Sep 8 '18 at 13:36
  • @TomHale no. readlink -f is not portable. What about the touch case? – Kusalananda Sep 8 '18 at 13:38
  • So [ "$file" = - ] && file=./- is needed before each invocation of GNU cat and touch for the truly paranoid? Any nicer ways? – Tom Hale Sep 8 '18 at 13:40
  • 4
    @TomHale If you're looping over files in a directory, make a habit of using for file in ./* or similar, instead of for file in *. – Kusalananda Sep 8 '18 at 13:43

Not the answer you're looking for? Browse other questions tagged or ask your own question.