20

I accidentally created a file with the name - (eg, seq 10 > -). Then I tried to use less to view it, but it just hangs.

I understand that this is happening because less - expects input from stdin, so it does not interpret the - as a file name. I tried less \- but it does not work either.

So, is there any way to indicate less that - is a file and not stdin?

The best I could get is:

find -name '-' -exec less {} +
fedorqui
  • 7,861
  • 7
  • 36
  • 74
  • 2
    @muru thanks for the comment, but the possible duplicate is so specific that I don't think it qualifies to be "exact duplicate". If it was reworded to something more generic like "how to access a file beginning with '-'", maybe. – fedorqui Jun 17 '15 at 09:29
  • 4
    That's not a duplicate question. The case for - alone is different. - is not an option. – Stéphane Chazelas Jun 17 '15 at 10:35
  • @StéphaneChazelas no but the solutions are the same as far as I can tell. – terdon Jun 17 '15 at 11:20
  • 1
    @terdon I don't think that having the same answer directly means they are duplicates. There was an example somewhere in Meta SO: if a question gets answered with "No", would anything whose correct answer is "No" be a duplicate? As I said before, it would be interesting to reword the candidate of duplicate so that it is more generic; otherwise, it doesn't make much sense to send there other questions like this one. – fedorqui Jun 17 '15 at 11:23
  • 1
    @fedorqui the issue here is that - is taken as an option. Yes, less takes the - alone as an option but most programs would choke on it for the same reason. Your basic question is "How can I use program foo on file -*". The dupe is about the same thing as far as I can tell. Just as explained there, you can use ./-, -- -, find, or the full path. We've been closing questions asking about file names starting with - against that one for a while, look at its linked questions. Wouldn't it have answered your question if you'd read it before posting? – terdon Jun 17 '15 at 11:29
  • 4
    @terdon, no. The accepted solution there won't work here. And no, - is not treated as an option, that's a totally different problem from the case of arguments that happen to be shaped like options. – Stéphane Chazelas Jun 17 '15 at 11:34
  • 1
    OK, question reopened pending discussion. – terdon Jun 17 '15 at 11:39
  • 2
    It is not necessary to enclose - between single quotes like '-' or escape it like \- because - is not a special character for common shells (at least POSIX compliant ones). The result is the same. – pabouk - Ukraine stay strong Jun 17 '15 at 12:06
  • mv -- - sensible_file_name – user1024 Jun 17 '15 at 22:31

3 Answers3

56

Just prefix it with ./:

less ./-

Or use redirection:

less < -

Note that since - (as opposed to -x or --foo-- for instance) is considered a special filename rather than an option, the following doesn't work:

less -- -   # THIS DOES NOT WORK
fedorqui
  • 7,861
  • 7
  • 36
  • 74
Howard
  • 5,209
  • It works! Is this because with ./ we specify the current directory? – fedorqui Jun 17 '15 at 09:22
  • 3
    BTW, that's what find -name '-' -exec less {} + runs. – Stéphane Chazelas Jun 17 '15 at 09:22
  • @fedorqui yes, . is relative parameter to specify pwd and .. refers to the current directory's parent directory. – Pandya Jun 17 '15 at 09:22
  • @StéphaneChazelas interesting! (Is there any reference about it?) However, I guess it has some maxdepth 1 is inherent. Otherwise, it would also print other files with - as a name in inner directories. – fedorqui Jun 17 '15 at 09:25
  • 6
    @fedorqui, no it's just that - and ./- (or /path/to/- or ../to/-) are two (4) valid paths to that - file, but a - argument is special to less (means read from stdin) while ./- is not special. – Stéphane Chazelas Jun 17 '15 at 09:26
  • @StéphaneChazelas did you mean execdir in that find example? – muru Jun 17 '15 at 09:27
  • 2
    @fedorqui find -name '-' -exec less {} + is the non-standard form for find . -name '-' -exec less {} +. It descends the tree at . and finds files and passes the paths of those files as arguments to less. Replace -exec less with -exec echo less to see what is being run. – Stéphane Chazelas Jun 17 '15 at 09:29
  • -- exists just for that purpose – haylem Jun 17 '15 at 09:30
  • @StéphaneChazelas brilliant! find . -name '-' -exec echo less {} + is very explanatory. Always learning from you. – fedorqui Jun 17 '15 at 09:32
  • BTW I don't understand why this answer got downvoted. It is very useful and right. – fedorqui Jun 17 '15 at 09:51
  • 4
    @haylem, -- is to mark the end of options. It won't help here. That - is not an option, it's a special non-option argument. See also https://unix.stackexchange.com/a/56370, https://unix.stackexchange.com/a/110756 – Stéphane Chazelas Jun 17 '15 at 10:16
  • @StéphaneChazelas: You're right, I had misread the question and thought the OP was asking about a filename starting with -, not that the exact filename (in its entirety) was -. – haylem Jun 17 '15 at 11:30
  • 3
    @haylem, try it for yourself. -- is handled by getopt() to mark the end of options, - is not recognised as an option by getopt(), so that - will be recognised as a normal argument whether -- is provided or not. And as a normal argument, less like most text utilities will treat it as meaning stdin, which we don't want here. – Stéphane Chazelas Jun 17 '15 at 11:32
  • @StéphaneChazelas: yes, yes, sorry. I have since deleted the comment you're replying to and posted a new one. I had not paid attention at all. Your interpretation is correct. – haylem Jun 17 '15 at 11:35
  • Your striked out command is badly formatted. the -- part is invisible because of the horizontal line. – Ruslan Jun 17 '15 at 15:47
3

Note: my answer is NOT valid in the OP's case, and only applies to tools following the convention mentioned below and not in the case of a file named exactly just - (dash), which is often also a special case to specify that reading from standard input is expected. See the accepted answer.

Leaving this here as it contains useful information for other cases that one may stumble upon while searching for answers.


Double-Dash it!

Use the standard double-dash (--) convention to indicate the last argument:

less -- -FILENAME

Example

$ echo "meh" > -badname
$ less -badname
Number is required after -b
$ less -- -badname # GREAT SUCCESS!

Whhhaattt?

This -- argument stems from a convention supported by most implementations of shell utilities and command-line tools, and most shells will visibly advocate that you should follow it when implemeing CLI tools.

Recommdended by the Open Group

The OpenGroup also mentions it in the Utility description defaults (v6) section of its Base Specification:

Default Behavior: [...] Standard utilities that do not accept options, but that do accept operands, shall recognize "--" as a first argument to be discarded.

The requirement for recognizing "--" is because conforming applications need a way to shield their operands from any arbitrary options that the implementation may provide as an extension. For example, if the standard utility foo is listed as taking no options, and the application needed to give it a pathname with a leading hyphen, it could safely do it as:

foo -- -myfile

and avoid any problems with -m used as an extension.

And in the Utility Syntax Guidelines (v7):

Guideline 10: The first -- argument that is not an option-argument should be accepted as a delimiter indicating the end of options. Any following arguments should be treated as operands, even if they begin with the '-' character.

Recommended by Bash

Here, excerpted from the bash manual, about its builtins supporting it:

Unless otherwise noted, each builtin command documented in this section as accepting options preceded by - accepts -- to signify the end of the options.

The :, true, false, and test builtins do not accept options and do not treat -- specially. The exit, logout, break, continue, let, and shift builtins accept and process arguments beginning with - without requiring --. Other builtins that accept arguments but are not specified as accepting options interpret arguments beginning with - as invalid options and require -- to prevent this interpretation.

Note that echo does not interpret -- to mean the end of options.

Additional Reading

haylem
  • 294
  • 2
    +1 : Most portable and command-independant solution, easy to remember, and considered the "best practice" – mveroone Jun 17 '15 at 09:32
  • 2
    @Kwaio any reference about the "best practice"? Just feel curious about it. – fedorqui Jun 17 '15 at 09:33
  • Except for cyberciti.biz articles, I don't know. it's just what I have always been taught to do by mentors/teachers/red hat training courses – mveroone Jun 17 '15 at 09:36
  • 1
    (nice edit, I suppose bash manpage is a good enough reference ^^) – mveroone Jun 17 '15 at 09:37
  • @fedorqui: I added the information. It's a standard convention specified by the Open Group shell command language specs, so most compliant shells would (and should) support it. Anything else just looks like dodgy hackery in my opinion. (though it's good to have another way if that's not supported, of course) – haylem Jun 17 '15 at 09:39
  • 2
    @Kwai: the Open Group is an even better one :) – haylem Jun 17 '15 at 09:39
  • 1
    This isn't a shell feature at all. The shell doesn't get involved here (except for built-in commands but that's shell involvement as a command not as the running shell). – Etan Reisner Jun 17 '15 at 10:02
  • @Etan: You're right, it's not, but it's a convention for shell utilities and for tools to respect that convention for consistency. – haylem Jun 17 '15 at 10:03
  • @EtanReisner: But you're right, I'll clarify my wording. – haylem Jun 17 '15 at 10:04
  • 24
    That answer applies to arguments that look like options. It's not the case for - which is not an option. Using ./- or redirection when possible is generally a better approach as it avoids other kinds of problems like the foo=bar of awk or that -. See also https://unix.stackexchange.com/a/56370, https://unix.stackexchange.com/a/110756 – Stéphane Chazelas Jun 17 '15 at 10:22
  • 7
    As Stéphane says, this does not answer the question. less -- - will still try to read from stdin. – Michał Politowski Jun 17 '15 at 11:11
  • @MichałPolitowski: Yes, I had misread the question, I didn't realize OP asked for a file named exactly -, I was thinking of filenames starting with -. – haylem Jun 17 '15 at 11:31
  • 5
    You may want to delete the answer (or move it to the other question as there are useful references in here) or make it clear that it doesn't apply to this particular case (and maybe explain why which would be even more useful). – Stéphane Chazelas Jun 17 '15 at 11:56
  • 1
    @StéphaneChazelas: Yes I was considering what to do. I'll edit when I get home. – haylem Jun 17 '15 at 12:29
  • @Kwaio this is certainly neither the most portable nor the most command-independent solution; in fact, it doesn't even work on the file in the question. ./- is the correct answer. -- is only "best practice" when using a wildcard at the start of the file name on commands that recognize it (which is not all, and for those gnu utilities that do, their counterparts on other systems may not). – Kevin Jun 18 '15 at 02:57
3

I would just mv - f && less f. Problem solved.

snth
  • 311