55

I somehow managed to create a file that doesn't seem to have a filename. I found some information regarding how to get more details of the file in the following thread.

However, I tried some of the suggestions listed and can't seem to delete the file. I'm not sure what I did to create it, but it happened while trying to copy an xml file.

Some info on the file is as follows;

> ls -lb
total 296
-rw-r--r--   1 voyager  endeavor  137627 Jan 12 12:49 \177

> file *
:               XML document

> ls -i
 417777   

I tried to find using the inum switch and then pipe that to rm as that seemed like the most foolproof way of getting rid of it. However, the example given at the bottom of the thread linked below failed for me. Example was:

> find -inum 41777 -exec ls -al {} \;
find: illegal option -- i
find: [-H | -L] path-list predicate-list

so I tried using the path list first like the following, but that didn't work either:

> find . -inum 41777 -exec ls -al {} \;

I'm not sure what the non-printable character \177 is or how I can pass that to an rm command, but I really want to make sure I don't mess up any other files/directories in my attempt to delete this file.

Mr Moose
  • 725

16 Answers16

54

The file has a name, but it's made of non-printable characters. If you use ksh93, bash, zsh, mksh or FreeBSD sh, you can try to remove it by specifying its non-printable name. First ensure that the name is right with: ls -ld $'\177' If it shows the right file, then use rm: rm $'\177'

Another (a bit more risky) approach is to use rm -i -- * . With the -i option rm requires confirmation before removing a file, so you can skip all files you want to keep but the one.

Good luck!

antje-m
  • 1,583
  • 1
    I suspect that rm -i * wouldn't have worked in this case; because of the newlines, the shell would expand * to something that rm wouldn't recognize as the file name. – Keith Thompson Jan 13 '12 at 05:32
  • 7
    @KeithThompson: Glob expansion in the shell isn't subject to further interpretation. There wouldn't be a newline in the expanded name unless it contained one. – Christoffer Hammarström Jan 13 '12 at 11:25
  • @ChristofferHammarström: Hmm. I'll do some experiments and comment further. – Keith Thompson Jan 13 '12 at 19:05
  • @ChristofferHammarström: There's a newline in the expanded name because the file name contains newline characters. But it seems I've underestimated bash and GNU rm. On my system, rm *, rm -i *, and rm Ctrl-V DEL TAB ENTER all work correctly, even if the file name contains a newline character (I used "\177foo\nbar\n"). Some commands don't handle such file names well, but the GNU tools seem robust; non-GNU versions of the same tools might not behave as well. In any case, the various tricks in these answers are useful to know. – Keith Thompson Jan 13 '12 at 20:29
  • Since the file name is only one character instead of rm -i -- *, you could do rm -i -- ?, then only have to evaluate the files with single-character names. – Justsalt Dec 17 '15 at 18:54
  • 1
    @KeithThompson, any shell will handle file globbing correctly, not only Bash, and rm never does any form of word splitting whether it's GNU rm or not. The shell expands the globs and, when it executes the command called for, it passes in the resulting filenames as null separated arguments (in the C code). What's dangerous is to pipe a list of files to something which uses newlines as delimiters; see Why is looping over find's output bad practice? – Wildcard Nov 15 '16 at 06:19
30

For those who use vim run it in the current working directory:

$ vim ./ 

and navigate to the file with the arrow keys or j/k. Then hit Shift+D and confirm deletion with y.

9

There's probably a way to pass the filename to rm, but if you're worried about messing anything up you can use a GUI file manager. Emacs comes with a directory editing mode you can use if you have it installed:

  1. Open the folder in emacs. You can run emacs /path/to/folder, or open emacs, hit Esc+x, and run dired, which will prompt for the path

    http://so.mrozekma.com/unix-dired-delete1.png

  2. Move to the row with the file you want to delete and press d. You should see a D in the left margin next to the file:

    http://so.mrozekma.com/unix-dired-delete2.png

  3. Press x to save your changes. It'll prompt to make sure you want to delete the file; press y:

    http://so.mrozekma.com/unix-dired-delete3.png

Michael Mrozek
  • 93,103
  • 40
  • 240
  • 233
  • 1
    Looks like a great idea, but I can't seem to find emacs on this machine. At the risk of starting a holy war...can this be done in vim? – Mr Moose Jan 13 '12 at 01:57
  • 2
    For those who use vim run it in the current working directory: vim ./ and navigate to the file with the arrow keys. Then hit Shift+D and confirm deletion with y. –  Jan 13 '12 at 02:01
  • @hesse Wow. Not something I would've expected vim to support – Michael Mrozek Jan 13 '12 at 02:02
  • 1
    @MichaelMrozek agreed, it has all these features that I occationally tend to discover. –  Jan 13 '12 at 02:11
  • @hesse. Vim 7.2 on my machine (SunOS) doesn't seem to support it. vim ./ opened vim and gave me - "./" Illegal file name. It's quite possible I don't have the latest version of Vim though. – Mr Moose Jan 13 '12 at 03:20
  • 1
    @Kevin nyan mode: http://nyan-mode.buildsomethingamazing.com/ – Tim Jan 13 '12 at 11:09
9

The file actually has a name, but it's made of non-printable characters.

An easy method is to rely on shell completion: press Tab repeatedly until the “strange” file name is inserted. You must have your shell configured to cycle between possible completions:

  • In bash, you need to invoke menu-complete, which is not bound by default, rather than complete, which is bound to Tab by default. Alternatively, use Esc * to insert completions for all files, and remove the ones you don't want to delete from the command line.
  • In zsh, the default settings are fine; you need to have setopt auto_menu or setopt menu_complete set.

If completion isn't helpful in your shell, you can call rm -i -- * and answer “no” except for this particular file. If the file's name doesn't begin with a printable character, you can restrict the matches to files whose first character isn't in a set of printable characters:

rm -i [!!-~]*
rm -i [![:print:]]*
rm -i -- [!a-z]

(Beware if your pattern may match a file called -f, which rm would treat as an option.)

Another method is to call ls -i to find the file's inode (an inode uniquely identifies a file on a given filesystem), then find -inum to operate on that particular file. This method is mostly useful when the directory contains a lot of files.

ls -i
# note the inode number 12345
find . -xdev -inum 12345 -delete

If your find doesn't have a -delete option, call rm:

find . -xdev -inum 12345 -exec rm -- {} \;

(Yes, I know that most of this has already been posted. But the answers with the relevant information are making this more complicated than it should be.)

7

You have already demonstrated with file * that the shell glob (*) is able to pick up this file, so now just do an rm on that glob.

  • Change to the directory the file is in.
  • Run rm -i *
  • Enter n for all the files except for the one with the seemingly invisible file name
phemmer
  • 71,831
4

Try using echo -e to get the character \177, then xargs to pass it to rm. echo doesn't take decimal escapes, so convert to hex: Turns out the 177 is octal; echo requires a \0 (literal, not null byte) before:

echo -ne '\0177\0' | xargs -0 rm
Kevin
  • 40,767
  • Are you saying that \xb1\0 is the hex representation of \177? – Mr Moose Jan 13 '12 at 01:57
  • @MrMoose \xb1 is decimal 177, \0 null-terminates it, and -0 tells xargs to take a null-terminated name instead of a newline-terminated one. – Kevin Jan 13 '12 at 02:02
  • I'm using bash on SunOS and when I tried your command, I got xargs: illegal option -- 0. I looked at the man page and couldn't see an option for null termination. I managed to get a fix for the issue now though. See the response marked as answer. – Mr Moose Jan 13 '12 at 03:18
  • Your echo, as-is, sends 2 file names to xargs, and then on to rm... the second file name is \n.. This results in either an error, or deleting a file of that name ('\n') – Peter.O Jan 13 '12 at 03:29
  • @perer Yes, I just noticed that. I added -n to get rid of the newline. – Kevin Jan 13 '12 at 03:34
  • .. another point.. you've referred to the 177 as decimal.. It is an octal value (the decimal value is 127) – Peter.O Jan 13 '12 at 04:12
  • @perer That crossed my mind, but shouldn't it have a 0 in front? – Kevin Jan 13 '12 at 04:18
  • The notation varies, depending on the environment.. just as in some environments 0xFF is valid as a hex value, and others may require \xFF... or even just FF.. or U+00FF ... – Peter.O Jan 13 '12 at 04:32
3

I can just about guarantee you that the file does have a name. It just happens to be a name that contains non-printable characters.

If rm didn't work, then using the output of find with the -inum option as an argument to rm isn't likely to help; it's just going to pass the same argument to rm, with the same problem.

The file is the only one in that directory, right?

The first thing I'd try is to cd into the directory, then type rm ./, then hit the Tab key. This should expand the argument to the actual name of the file, and the preceding ./ should prevent rm from interpreting the argument as something other than a file name.

Another solution is to cd to the parent directory, then use rm -r (or, if necessary, rm -rf) on the directory. That should remove the directory and all its contents, regardless of name. You can then use mkdir to recreate the directory (and perhaps chmod to restore its permissions).

EDIT :

Looking at your ls -lb output again, I think that the file name starts with an ASCII DEL character (which is bad not not fatal) and contains one or more newline characters (which is really bad). As far as I can tell, the rm command can't interpret a newline as part of a file name.

But the unlink() system call can. Here's a Perl script I threw together that will iterate through the files in the current directory, and ask you for each one whether you want to remove it. It doesn't use an external command like rm to remove the files, so it should be able to handle any funny characters that the file system supports (anything other than ASCII NUL and /).

Doing rm -r or rm -rf on the containing directory should still work, but if you just want to remove a single file you can try this.

#!/usr/bin/perl

use strict;
use warnings;

opendir my $DIR, '.' or die ".: $!\n";
my @files = sort grep { -f } readdir $DIR;
closedir $DIR;

foreach my $file (@files) {
    my $pfile = printable($file);
    print "Remove $pfile? ";
    my $answer = scalar <STDIN>;
    if ($answer =~ /^[Yy]/) {
        print "Removing $pfile\n";
        unlink $file or warn "Unable to remove $pfile: $!\n";
    }
}

sub printable {
    my($s) = @_;
    $s =~ s/[^ -~]/?/g;
    return $s;
}

(Another solution, if there are other files in the directory that you want to keep, is to move all the other files into a temporary directory, then nuke and recreate the directory and move the other files back.)

(Oh, and whatever you did to create that file, try not to do it again!)

manatwork
  • 31,277
2

Here's how to delete the file by inode number.

Find the inode number of the file you want, using ls -li. The inode number will be in the first column of output.

For example:

$ls -li
311010 -rw-rw-r-- 1 me me 3995 Apr  6 16:27 -???\# ;-)

In this case, the inode number is 311010

Use find to look for the file by inode number.

find . -inum 311010 

Make sure that find only returns the name of the file you want to delete. For example:

$find . -inum 311010
./-???\# ;-)

Once you are sure that find is finding the right file, then pass the -delete parameter to find. It will delete the file for you.

find . -inum 311010 -delete

On Solaris, an anonymous user suggested this:

find . -inum 311010  -exec rm -i {} \;
Christian Long
  • 153
  • 1
  • 7
1

If you can find exactly that file using find command, then you can use -delete predicate. Just be careful, and make -delete a last parameter of find command or it will hurt a lot...

1

The other answers are great and will work in almost all environments.

But, if you happen to have a gui running, just open the directory in nautilus, dolphin, konqueror, or your favorite file manager and you should be able to easily see and delete any file that has an uncooperative name with a couple of mouse clicks.

If you're not a regular emacs or vim user (as featured in several other answers), this might be the easiest way to go.

Joe
  • 1,368
1

Just in case it helps anyone I'll add my two cents worth. I was able to get the file name(s) using stat on the wildcard like this:

stat -c %N *

Output:

`\220g\201\acontexts'
`\220g\201\alogins'
`\220g\201\amodules'
`\220g\201\apolicy'
`\220g\201\asetrans.conf'

Then I could use ANSI C-quoted strings (as used in the accepted answer) to access the files:

rm $'\220g\201\asetrans.conf'
miken32
  • 466
0

Had a similar problem with a file named "\033" (the actual "Escape" character).

for x in $( ls | awk '!/^[A-Z]/ && !/^[a-z]/ && !/^[0-9]/');do rm -i -- $x ; done

The above will prompt you to remove files in the current directory that do not start with a letter or number.

Signal15
  • 101
0

Instead, you can rename the current directory to DirX_OLD , create a new directory with the name DirX and move all the required files from DirX_OLD to DirX. After copying the files you can delete DirX_OLD

PersianGulf
  • 10,850
0

Every file has a name, you just need to find exactly which.

I'll use this files as examples:

$ touch $'\177' $'\001\002' $'\033\027' a b abc $'a\177b'

Those files will show as ? in normal ls:

$ ls
??  ?  ?002  a  abc  b

But if your ls allow the -Q option it should be clear what the real file name is:

$ ls -1iQ *
26739122 "\001\002"
26739117 "\033\027"
26739115 "\177"
26739118 "a"
26739121 "a\177b"
26739120 "abc"
26739119 "b"

those are the inode numbers and "Quoted" names in 1 column of the files in pwd.

That is using the hability of a (POSIX) shell to use expansions (globbing):

To have the names with any non-printing character:

$ ls -1iQ *[![:print:]]*
26739122 "\001\002"
26739117 "\033\027"
26739115 "\177"
26739121 "a\177b"

And to list files that may have only non-printing characters:

$ ls -1iQ [![:print:]]*
26739122 "\001\002"
26739117 "\033\027"
26739115 "\177"

Then, to selectively (the -i (interactive) option) remove them use:

$ rm -i [![:print:]]*
rm: remove regular file ''$'\001\002'? n
rm: remove regular file ''$'\033\027'? n
rm: remove regular file ''$'\177'? n

You just need to choose which ones to erase by answering y to the question above.

0

If you have a bash with autocompletion, this may work for you. I typed the character that was shown in the directory listing. Then I triggered autocomplete and renamed the thing to something printable.

Here is what worked for me:

$ ll
-rw-r--r--.  1 xxxxxxxx  xxxxx 25608 Oct 11 11:11 ?
$ mv ?  
(hit the [tab] and it gets extended to ...)
$ mv ^[
(now complete the command and rename to something printable)
$ mv ^[ weirdchar
$ ll
-rw-r--r--.  1 xxxxxxxx  xxxxx 25608 Oct 11 11:11 weirdchar
(now delete)
$ rm weirdchar
Sascha
  • 101
-4
rm -dfvr /"(null)" 

worked for me.

Anthon
  • 79,293
Ryan S
  • 1