20

A badly-written script created a directory named '--' (including the single quotes) in my home directory.

When I cd to that directory, I am brought back to my home directory.

I'd like to remove that item, but cannot figure out how to do it. Escaping some or all of the characters in the directory name, returns No such file or directory.

Linux version

Linux version 5.11.0-1022-aws (buildd@lgw01-amd64-036) (gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0, GNU ld (GNU Binutils for Ubuntu) 2.34) #23~20.04.1-Ubuntu SMP Mon Nov 15 14:03:19 UTC 2021
0

6 Answers 6

44

In your case, since you actually have the quotes as part of the name, you can just do:

rm -r \'--\'

Or

rmdir \'--\'

A more common situation is that the quotes are not part of the name so you also need to deal with the fact that the name looks like an option (starts with a -). In such cases, the classic approaches are:

  1. Use -- to signify the end of options so that anything after the -- will not be parsed as an option even if it starts with -:

     rmdir -- "'--'"
    
  2. Use a full path or just ./ (but you also need to quote the name to protect the ' from the shell):

     rmdir ./"'--'"
    
  3. Use GNU find:

     find . -name "'--'" -delete
    
  4. Use something else. Like Perl:

     perl -e "rmdir(\"'--'\")"
    

Note that all of the above assume the directory is empty. If it isn't, just use one of these instead:

rm -r ./"'--'"

or

rm -r -- "'--'"
2
  • 2
    If rmdir \'--\' works, rmdir "'--'" will also work without needing a -- delimiter, because both pass an identical arg list to rmdir. The option doesn't start with a -. Dec 17, 2021 at 17:10
  • 3
    @PeterCordes yes indeed. Parts of this answer were written before it was clear that the quotes were included in the name.
    – terdon
    Dec 17, 2021 at 17:35
28

For "funny" names with quotes, backslashes, spaces, newlines etc, as the other answers show, quoting and/or escaping is needed.

However, if you are trying to use the filename interactively from the terminal, instead of a script, tab completion can be really helpful in doing the quoting for you.

With the directory name you have, just type:

rm -r \'<tab>

where <tab> is the tab key, on the left of letter Q on the keyboard. We use \' to escape the first single quote character, since it is a special character.

If there aren't any other files/directories starting with a single quote character, bash will complete the file name, correctly escaped:

rm -r \'--\'/

Note: There seems to not be a need to add the 'end-of-options' sequence -- as suggested by the other answers, because the first letter of the filename is not a - minus character.

6
  • 1
    Thank you. That worked. Dec 15, 2021 at 16:22
  • 2
    Great, @KarlHakkarainen. Please accept the answer by selecting the checkmark icon. That helps the site work. :) See stackoverflow.com/help/someone-answers Welcome to Unix and Linux StackExchange. Dec 16, 2021 at 16:20
  • 2
    As well as tab-completion, glob expansion is another handy trick for files where you're not sure how to type or quote the first character easily. rm -i *--* should work nicely, and the -i lets you interactively skip any matches you don't want to remove. Dec 17, 2021 at 17:13
  • @PeterCordes: That's a good suggestion, perhaps you should post it as a separate answer so it gets the visibility it deserves.
    – user000001
    Dec 17, 2021 at 20:06
  • I was thinking it was related to your answer, and you might want to edit it in. I can post a separate answer covering tricks like this for the general case (of filenames with some easy-to-type characters) if you don't want to include it. Dec 18, 2021 at 0:39
12
$ mkdir -- \'--\'
$ ls
'--'
$ rmdir -- \'--\'
$ ls
$
1
  • If you're going to show ls, you can show ls -Q to quote the filenames if they need it, like ls --quoting-style=shell. Dec 17, 2021 at 17:19
7

My preferred method of dealing with filenames that cannot be typed or tab-completed (nonprinting characters for instance) is to delete them by inode. The ls flag -i adds each file's inode number to the output.

$ ls
bar.c  foo.c

$ ls -i
5646079 bar.c  5642988 foo.c

$ find . -maxdepth 1 -inum 5642988 -delete

$ ls
bar.c

Here is an example with a file whose name consists of unprintable characters only:

$ ls -il
total 0
5646079 -rw-rw-r-- 1 oem oem 0 Dec 16 09:49 bar.c
5642988 -rw-rw-r-- 1 oem oem 0 Dec 16 10:02 foo.c
5646169 -rw-rw-r-- 1 oem oem 0 Dec 16 10:05

$ find . -maxdepth 1 -inum 5646169 -delete

$ ls -il
total 0
5646079 -rw-rw-r-- 1 oem oem 0 Dec 16 09:49 bar.c
5642988 -rw-rw-r-- 1 oem oem 0 Dec 16 10:02 foo.c

Do note that the order of the arguments to find is important.

Also, if there are multiple hard links to the file in the same directory, this will remove them all.

6
  • 1
    I would add -maxdepth 0 to the find command line so you're not recursing through a potentially large file system.
    – doneal24
    Dec 16, 2021 at 16:04
  • This answer would benefit from a discussion of how this technique interacts with hardlinks
    – Ben Voigt
    Dec 16, 2021 at 17:03
  • @doneal24 Actually it's -maxdepth 1 but you are 100% correct.
    – dotancohen
    Dec 16, 2021 at 17:06
  • 3
    I've not tried to use this technique, but based on your explanation I predict it will remove as many names for the same file as it can find, and not just the offending/unwanted one.
    – Ben Voigt
    Dec 16, 2021 at 17:15
  • 2
    @dotancohen: Hmm, we're talking about hardlinks, so by definition within the same filesystem. And I agree -maxdepth 1 will prevent removing other names (directory entries) outside the specified directory, but there can be multiple hardlinks within a directory too.
    – Ben Voigt
    Dec 16, 2021 at 19:15
2

The GNU coreutils version of ls (which is what you're likely to see on Linux) has the --quoting-style option, which controls how it shows names with unprintable or otherwise funny characters. If set to shell-escape, it uses the various shell quotes to make the output more readable and valid as input to a shell. That output can be copypasted to the shell command line as an argument to rm.

Though note that it uses $'...' quotes to make special characters more readable, and while most shells support it by now, not all might. In particular, Dash, the shell used as /bin/sh on Debian and Ubuntu, doesn't seem to support it. Bash, ksh and zsh definitely do, though.

Here, the second file is your '--', quoted with double quotes, and the other one is an abomination with characters like Ctrl-A, tab, newline, a single quote and a trailing space. There it uses $'..' and backslash-escapes.

$ ls --quoting-style=shell-escape -1
''$'\001\t'\''foo'$'\n''bar '
"'--'"

(ls doesn't seem to try to minimize the output much, the first could be printed as just $'\001\t\'foo\nbar ')

Either or both can be removed by using that output:

$ rm -- ''$'\001\t'\''foo'$'\n''bar '
$ rm -- "'--'"

Note that you still need to use -- if the filename starts with a dash, even if ls shows that dash quoted.

I think shell-escape is also the default quoting style used by recent-ish versions of ls (at least the output looks like it), so you don't even actually need to spell out the long option.

2
  • Thanks for this tip. I edited your answer to replace '..' with '--'. I believe my edit is correct, but you might want to check it out.
    – Totor
    Dec 18, 2021 at 0:28
  • @Totor, oh, oops. thanks for the edit.
    – ilkkachu
    Dec 18, 2021 at 8:21
1

First off: NEVER use an unconditional and destructive delete when trying to do something like this. If you get it wrong, it could delete many things. Use either rm -i <name> or rmdir <name>

Second: the fact that cding to it brings you back to your home directory means you are writing cd '--' which the shell translates to cd --, then considers the -- to be the end of the option arguments. You might try cd -- -- or cd ./--. A ./ on the beginning of a filename will prevent it being interpreted as an option.

Third: command completion is your friend. In bash or tcsh (and probably several other shells), if you get the first character or two in place and press TAB, it will either fill in the rest or perhaps list the choices. Try rmdir ./\' or rmdir ./-, either way followed by a tab. If it gets you the right name, the rmdir should succeed.

Fourth: the single-quotes you are seeing might be part of the name, or might be an artifact of how you are listing the files. So your final command should probably be either rmdir ./-- or rmdir ./\'--\'.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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