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.

8
  • 1
    \177 is the ASCII DEL character. Jan 13, 2012 at 1:57
  • 9
    417777 != 41777
    – user
    Jan 13, 2012 at 9:22
  • Excellent point Michael. That is probably why my command never worked.
    – Mr Moose
    Jan 13, 2012 at 15:33
  • @KeithThompson Is it octal without a leading 0?
    – cat
    Oct 2, 2016 at 1:42
  • 1
    @cat: In this context, yes. It follows C syntax for string and character literals. Oct 2, 2016 at 10:17

16 Answers 16

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!

6
  • 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. Jan 13, 2012 at 5: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. Jan 13, 2012 at 11:25
  • @ChristofferHammarström: Hmm. I'll do some experiments and comment further. Jan 13, 2012 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. Jan 13, 2012 at 20:29
  • 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, 2016 at 6: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.

1
  • i generally get scared of vim, but that worked really well
    – tofutim
    Mar 11, 2018 at 0:15
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

6
  • 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, 2012 at 1: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.
    – user13742
    Jan 13, 2012 at 2:01
  • @hesse Wow. Not something I would've expected vim to support Jan 13, 2012 at 2:02
  • 1
    @MichaelMrozek agreed, it has all these features that I occationally tend to discover.
    – user13742
    Jan 13, 2012 at 2:11
  • 1
    @Kevin nyan mode: nyan-mode.buildsomethingamazing.com
    – Tim
    Jan 13, 2012 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
0
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
8
  • Are you saying that \xb1\0 is the hex representation of \177?
    – Mr Moose
    Jan 13, 2012 at 1: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, 2012 at 2: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, 2012 at 3: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, 2012 at 3:29
  • @perer Yes, I just noticed that. I added -n to get rid of the newline.
    – Kevin
    Jan 13, 2012 at 3:34
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!)

0
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 {} \;
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.

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'
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.

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

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
-4
rm -dfvr /"(null)" 

worked for me.

0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .