Reputation: 1
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
Reputation: 185053
LANG=C diff -aqr /tmp/d1 /tmp/d2 |
awk -F'[ :]' '/^Only in/{system("cp -a "$3"/"$NF " /tmp/d3/")}'
LANG=C
force diff output to be in English for erliable parsingdiff -aqr /tmp/d1 /tmp/d2
will display the files that are missing or different from both dirs recursivelyawk
to cut out the dir/filenames-F'[ :]'
means : spliting columns on spaces and :
is like a
grep`, and if the pattern is found, we execute :system("cp -a "$3"/"$NF " /tmp/d3/")
: copy found_dir/found_file in /tmp/d3Upvotes: 0
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 sed to get only the path to the file (maybe diff
has an option to do this and we don't need sed:
$ 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 sed 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
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