How to `touch` and `cat` file named `-`

6,383

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.

Share:
6,383
Tom Hale
Author by

Tom Hale

Updated on September 18, 2022

Comments

  • Tom Hale
    Tom Hale almost 2 years

    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 -?

  • Tom Hale
    Tom Hale almost 6 years
    It seems the way for absolute belts and braces would be to do cat "$(readlink -f "$file")"
  • Tom Hale
    Tom Hale almost 6 years
    What about the touch case though?
  • Kusalananda
    Kusalananda almost 6 years
    @TomHale no. readlink -f is not portable. What about the touch case?
  • Tom Hale
    Tom Hale almost 6 years
    So [ "$file" = - ] && file=./- is needed before each invocation of GNU cat and touch for the truly paranoid? Any nicer ways?
  • Kusalananda
    Kusalananda almost 6 years
    @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 *.
  • Stéphane Chazelas
    Stéphane Chazelas almost 6 years
    @TomHale, Also "$(readlink -f -- "$file")" (here adding the missing --) strips trailing newline characters so wouldn't work for files that end in newline. Also note that with GNU readlink at least, readlink -f foo/bar where foo doesn't exist returns an empty output, exit status 1 and no error message.