9

I copied a directory using this:

cp -r dir/ ../../

without thinking and realized that it copied the contents of dir instead of actually dir to the above directory. Now I have a mess of files I need to delete so I can redo the cp correctly. How can I delete all the files that I mistakenly copied over if they don't match anything in particular?

nopcorn
  • 9,559

4 Answers4

12

Ok, this is basically the same as klapaucius' answer. (And it won't repair any damage done if cp has overwritten some existing files.)

Assuming you are in the source directory (in dir). This command:

find . -type f -exec echo '{}' ';'

will list all the files (recursively) present in your dir directory (quite like -print). The -type f option is there to prevent the listing of sub-directories.

So, if you use:

find dir -type f -exec echo '../../../{}' ';'

This should list corresponding files (copies) in the target directory.

Now if the list is correct, you will be able to remove the copies using:

find dir -type f -exec rm -- '../../../{}' ';'

As for pruning remaining empty directories that come from the cp… hum…

Tony
  • 103
  • I'm using the echo method to double check the file names first. However, I need to change dir to . or else it doesn't seem to work. Also, it only lists the files in dir, not the ones also in ../.. – nopcorn Aug 31 '11 at 13:11
  • Ah yes, I got confused by your description. Why did cp copy the content and not the directory in this case? – Stéphane Gimenez Aug 31 '11 at 13:16
  • No clue, I ran cp -r ./dir/ ../../ – nopcorn Aug 31 '11 at 13:18
  • 1
    Ah! that's cause of the final /! – Stéphane Gimenez Aug 31 '11 at 13:24
  • 1
    The one in ./dir/? Should it have been ./dir instead? – nopcorn Aug 31 '11 at 13:27
  • 1
    Yes, but this happens only with some cp versions it seems… Mine (coreutils 8.12) has not this behavior anymore. (It was useful sometimes.) – Stéphane Gimenez Aug 31 '11 at 13:31
  • In such cases what I do is to send the filelist to some temporary file, say /tmp/lst, which I can check carefully (or even edit), and when I'm OK, I go rm -f $(< /tmp/lst) (the $(<is a bash(1)ism, in standard sh(1) it would be the cumbersome rm -f \cat /tmp/lst` – vonbrand Jan 23 '13 at 16:40
  • @stéphane Assuming existing directories a and b:

    Saying cp -r a b means "copy the directory named a into directory b using the same name a under b", so you create b/a and put all the files under it.

    Saying cp -r a/ b is the same as cp -r a/. b which also means "copy the directory named a/. into directory b using the same name . under b". Of course you don't create b/. because it's already there, and so all the files go under b/. not under b/a.

    – Ian D. Allen Mar 28 '14 at 10:49
  • @StéphaneGimenez Yes, I see now that some versions of cp take a/ to mean a/. and some don't. Ubuntu 12.04 does; CentOS 6.5 doesn't. Sigh. – Ian D. Allen Mar 29 '14 at 02:33
2

Use find in combination with -exec. Better test with ls before like this:

find . -name "*" -exec ls ../../{} \;
1

The following code handles file names which contain embedded newlines I've put a full version script on paste.ubuntu. It checks for existance and matching sizes... The code to generate the test data is also included there.

# This lists the original fully-qualified filename, 
# and its would-be counterpart (assuming it exists) 
unset a i
while IFS= read -r -d $'\0' relf; do
    printf "%s\n$s" "$PWD${relf:1}" "$(dirname "$(dirname "$PWD")")${relf:1}"
done < <(find  . -type f  \! -name '.' -name '*' -print0)

Here is a sample output of the full version (mentioned above), where one file is missing, and another has a different file size to the original

WARNING -----
NOT in target   ./file1
ok --------
sizes match     ./c/file4-in-subdir
WARNING ------
size mis-match  ./file3   is   triple-spaced
ok --------
sizes match     ./file2
has newline!
Peter.O
  • 32,916
-2

You want to remove into PATH_DIR1 existing files in PATH_DIR2 :

find PATH_DIR1 -type f  -exec basename '{}' ';' | xargs printf -- 'PATH_DIR2/%s\n' | xargs rm

Explanations :

  1. List all filenames in PATH_DIR1

find PATH_DIR1 -type f -exec basename '{}' ';'

  1. Filter the previous listing with the existing files in PATH_DIR2 (find the intersect data)

xargs printf -- 'PATH_DIR2/%s\n'

  1. Execute the remove action to the filtered result

xargs rm

jteks
  • 97
  • 1
    Would you mind describing the various steps of your pipeline while also mentioning any restrictions or pitfalls (filenames containing spaces may be an issue, for example)? Are you deliberately skipping over directories? – Kusalananda Nov 11 '23 at 06:33