20

I have ubuntu file system directories in the root directory and I accidentally copied hundreds of files into root directory.

I intuitively tried to remove copied files by excluding file system like

rm -rf !{bin,sbin,usr,opt,lib,var,etc,srv,libx32,lib64,run,boot,proc,sys,dev} ./.

bu it doesn't work. What's the proper way to exclude some directories while deleting the whole?

EDIT: Never try any of the commands here without knowing what to do!

jimmij
  • 46,064
  • 19
  • 123
  • 136
kenn
  • 733
  • 2
  • 11
  • 22
  • 1
    What shell are you using? Filename globbing is shell dependent when it comes to doing fancy stuff like that. – Kusalananda Jan 06 '17 at 21:02
  • 2
    Not an answer, but I would use for example `mc`. Select everything with `*`, then deselect specific files with `-` or `Insert` key. – pbm Jan 06 '17 at 21:05
  • 1
    Can you explain what 'mc' is? – MikeP Jan 06 '17 at 21:12
  • @Kusalananda it's `/bin/bash` – kenn Jan 06 '17 at 21:15
  • 3
    @MikeP: `mc` is Midnight Commander, a very useful imitation of Norton Commander. It runs in a terminal. (Both Midnight Commander and Norton Commander are examples of what is called an [orthodox file manager](https://en.wikipedia.org/wiki/File_manager#Orthodox_file_managers).) – AlexP Jan 06 '17 at 21:18
  • You copied the files from somewhere... Do they still exist in that original place (ie not just in `/`)? If so you can use that to determine the files to be deleted. I can write an answer with this tonight – roaima Jan 07 '17 at 09:09
  • @roaima files got mixed, for example it copied subdirectories of `/etc` into `/` root – kenn Jan 07 '17 at 11:49
  • Never use `-f` with `rm` unless you absolutely have to! O.o; – Mio Rin Jan 07 '17 at 15:42

3 Answers3

24

Since you are using bash:

shopt -s extglob
echo rm -rf ./!(bin|sbin|usr|...)

I recommend to add echo at the beginning of the command line when you are running something what potentially can blow up the entire system. Remove it if you are happy with the result.

Note: The above command won't remove hidden files (those which name start by a dot). If you want to remove them as well then activate also dotglob option:

shopt -s dotglob
jimmij
  • 46,064
  • 19
  • 123
  • 136
  • I used it to clean up the filesystem successfully. At first I tested it in a directory. I also tried `rm -rf ./!(bin,sbin,..)` comma seperated, but it removes all directories. – kenn Jan 07 '17 at 14:25
  • What is that `!(...)` syntax? From the example I intuitively thought `ls ./!(*/)` would list only files, but it doesn't work that way. – giusti Jan 07 '17 at 14:38
  • @giusti This syntax is extended pattern matching in filename expansion mechanism - you will find its description in the bash manual under Pathname Expansion chapter. It cannot be used to exclude files' types, only names, just like with star `*` or other globs. – jimmij Jan 07 '17 at 16:51
12

This command will show all non-directories in /:

find / -maxdepth 1 -type f

Once you have made absolutely sure no files are there that you wish to keep, you can use:

find / -maxdepth 1 -type f -delete

Safer, would be to move them elsewhere to ensure you aren't deleting something you want to preserve:

mkdir /root/preserve
find / -maxdepth 1 -type f -exec mv -- "{}" /root/preserve/\;

If, in addition to files, you also have directories that you've added to the root of the filesystem, this could be automated by excluding the LSB directories from an automated mv or rm, but honestly, since we're dealing with purging things in the root of the filesystem, I would strongly suggest you consider doing it manually if at all feasible.

If this is not feasible, something like this could do the trick:

#!/bin/bash
declare -a excludes
for item in root sys 'lost+found' mnt home proc etc opt boot lib lib64 libx32 sbin media srv dev var usr bin tmp run; do
    excludes+=("$item")
done
if ! [[ -d /root/preserve ]]; then
    mkdir -p /root/preserve
fi
IFS="\n"
for item in find / -type d -maxdepth 1; do
    really=true
    for exclude in ${excludes[@]}; do
        if [[ "$exclude" == "${item#/}" ]]; then
            really=false
        fi
    done
    if [[ "true" == "$really" ]]; then
        mv -- "$item" /root/preserve/
    fi
done

Once you've passed the scream test (i. e. your system still runs and you are not screaming in anguish), you can remove the contents of /root/preserve/.

Important note: Whatever you do, don't even think about running any permutation of rm -fr [ANYTHING GOES HERE] /.

Vlastimil Burián
  • 27,586
  • 56
  • 179
  • 309
DopeGhoti
  • 73,792
  • 8
  • 97
  • 133
  • your solution only deletes files but there are folders around too. – kenn Jan 06 '17 at 21:31
  • 1
    One too many times, I have heard shrieks of dismay from a poorly-aimed `rm`. Like a script with `rm -fr $somepath/` with `somepath` uninitialized in it. I could have gone further in expressing how grave this is, but opted to remain family friendly in my admonition. – DopeGhoti Jan 06 '17 at 22:44
3

This should get the job done (though not in the same way as the OP asks):

ls -1 >1.txt
pico 1.txt 

remove all files/directories you want to keep

xargs rm < 1.txt

If your files all have the same name format, date, or something else, then there are other methods.

I'd look at the inodes - and see if they are sequential via ls -i |sort and if they are, then the new files will have larger inodes. Then using the same type of process as above...

ls -iF1 | sort |cut -c10- | grep -vE "\/|\@" >i.txt   #This part removes entries that are not regular files, such as directories and links.  
pico i.txt
xargs rm < i.txt

in the cut command above, check your inodes list first to make sure it is the right amount.

MikeP
  • 216
  • 1
  • 5