trizzle
trizzle

Reputation: 1

Bash script that copies the differences in two directories to a third directory?

What I want to do is compare two directories, d1 and d2, and copy all files that are not present in both d1 & d2 to a third directory, d3.

I know that I can copy all files in both directories using this:

cp -vfudp dir1/* dir3/

cp -vfudp dir2/* dir3/

But I know I need to incorporate the find function and compare. Or diff. Could I use

rsync -a dir1/ dir2/

to somehow create a third directory and put the differences?

I think I need to use

diff -r --brief dir1 dir2

somewhere in there too. I'm a novice programmer, and new to bash too, so explanations with the functions would be much appreciated.

Upvotes: 0

Views: 1212

Answers (3)

Gilles Quénot
Gilles Quénot

Reputation: 185053

A one liner solution using and

LANG=C diff -aqr /tmp/d1 /tmp/d2 |
    awk -F'[ :]' '/^Only in/{system("cp -a "$3"/"$NF " /tmp/d3/")}'

Explanations

  • LANG=C force diff output to be in English for erliable parsing
  • diff -aqr /tmp/d1 /tmp/d2 will display the files that are missing or different from both dirs recursively
  • we use awk to cut out the dir/filenames
  • -F'[ :]' means : spliting columns on spaces and :
  • '/^Only in/is like agrep`, and if the pattern is found, we execute :
  • system("cp -a "$3"/"$NF " /tmp/d3/") : copy found_dir/found_file in /tmp/d3

Warning

  • the script don't handle file/dir with space in names, this require extra processing, but you have a good start now

Upvotes: 0

pfnuesel
pfnuesel

Reputation: 15310

for file in $(diff dir1 dir2 | sed 's/Only in \([^ ]*\): /\1\//'); do
    cp "$file" dir3/
done

diff dir1 dir2 gives you a list of files that are not present in both directories:

$ diff dir1 dir2
> Only in dir2: f3
> Only in dir1: f4
> Only in dir2: f5

We pipe this through to get only the path to the file (maybe diff has an option to do this and we don't need :

$ diff dir1 dir2 | sed 's/Only in \([^ ]*\): /\1\//'
> dir2/f3
> dir1/f4
> dir2/f5

Then we loop over this list of files and copy them to dir3.

Careful with subdirectories! (Do you have any?)

Let's have a closer look at the command:

sed 's/Only in \([^ ]*\): /\1\//'

s is the substitute command, if you e.g. want to substitute true with false, you'd use s/true/false/. We want to substitute Only in \([^ ]*\):. The first part is just the output text from the diff command, then we use the escaped brackets \( and \) to match the name of the file, in order to reference to it later again (with \1). Inside the brackets we have [^ ]*, which matches everything except a space (so here we are assuming that the filenames have no spaces, this could be improved!). This matches the name of the directory. Then we match some more text of the diff command, namely :. All this is replaced with \1, which is just the part inside \( and \) (the name of the directory). We end up with the full path to the file in question.

Upvotes: 1

dj_segfault
dj_segfault

Reputation: 12419

d1="/tmp/d1"
d2="/tmp/d2"
d3="/tmp/d3"

cd "${d1}"
for file in * ; do
    if [ ! -f "${d2}/${file}" ] ; then
        cp "${file}" "${d3}"
    fi
done
cd -

cd "${d2}"
for file in * ; do
    if [ ! -f "${d1}/${file}" ] ; then
        cp "${file}" "${d3}"
    fi
done
cd -

Upvotes: 0

Related Questions