views:

304

answers:

5

I have a manifest file which is just a list of newline separated filenames. How can I remove all files that are not named in the manifest from a folder?

I've tried to build a find ./ ! -name "filename" command dynamically:

command="find ./ ! -name \"MANIFEST\" "
for line in `cat MANIFEST`; do
    command=${command}"! -name \"${line}\" " 
done
command=${command} -exec echo {} \;
$command

But the files remain.

[Note:] I know this uses echo. I want to check what my command does before using it.

Solution:(thanks PixelBeat)

ls -1 > ALLFILES
sort MANIFEST MANIFEST ALLFILES | uniq -u | xargs rm

Without temp file:

ls -1 | sort MANIFEST MANIFEST - | uniq -u | xargs rm

Both Ignores whether the files are sorted/not.

+2  A: 

Using the "set difference" pattern from http://www.pixelbeat.org/cmdline.html#sets

(find ./ -type f -printf "%P\n"; cat MANIFEST MANIFEST; echo MANIFEST) |
  sort | uniq -u | xargs -r rm

Note I list MANIFEST twice in case there are files listed there that are not actually present. Also note the above supports files in subdirectories

pixelbeat
I get `rm: missing operand` on your command. I just figured it out using ls, comm, and xargs. Thanks anyway pb
brice
Add a -r to xargs to remove that warning (when no files are needed to be deleted)
pixelbeat
Set difference pattern using `grep` instead: `find ./ -type f -printf "%P\n" | fgrep -vf MANIFEST |...`
Mark Edgar
A: 

figured it out:

ls -1 > ALLFILES
comm -3 MANIFEST ALLFILES | xargs rm
brice
Yes comm -3 is equivalent to my solution above. But be careful as comm expects already sorted inputs
pixelbeat
Cheers, eneded up using the set difference of unsorted.
brice
A: 

Assumes that MANIFEST is already sorted:

find -type f -printf %P\\n | sort | comm -3 MANIFEST - | xargs rm
Dennis Williamson
A: 

Just for fun, a Perl 1-liner... not really needed in this case but much more customizable/extensible than Bash if you want something fancier :)

$ ls
1   2   3   4   5   M
$ cat M
1
3
$ perl -e '{use File::Slurp; %M = map {chomp; $_ => 1} read_file("M"); $M{M}=1; \
foreach $f (glob("*")) {next if $M{$f}; unlink "$f"||die "Can not unlink: $!\n" };}' 
$ ls
1   3   M

The above can be even shorter if you pass the manifest on STDIN

perl -e '{%M = map {chomp; $_ => 1} <>; $M{M}=1; \
foreach $f (glob("*")) {next if $M{$f};unlink "$f"||die "Can not unlink: $!\n" };}' M
DVK
+3  A: 

For each file in current directory grep filename in MANIFEST file and rm file if not matched.

for file in *
  do grep -q -F "$file" PATH_TO_YOUR_MANIFIST ||  rm "$file" 
done
Jürgen Hötzel