Finding difference between 2 directories in linux - linux

Hi guys,
I was trying to find the difference between two directories, dir structure as follows.
dir1/subdir/file.txt
dir2/subdir/nffile.txt
when i tried with "diff -r dir1 dir2" it shows only the content difference in files but i want interns of new file addition.
Any possibility that we can find the difference between these dir using the "diff" command.

Try out using --brief option something like:
$ diff --recursive --brief dir1 dir2
Only in dir1/dir1: file1
Only in dir2/dir1: file2
Only in dir1: dir2
$

Alt.1 - use of diff
diff <(ls dir1) <(ls dir2)
Alt.2 - script without use of diff
for i in /my/directory/*; do
name=$(basename "$i")
if [[ ! -e "/my/other/directory/$name" ]]; then
echo $name not found in other directory
fi
done

diff -qrN dir1 dir2
if one of the directories has a name that can be confused with a paramenter
diff -qrN -- -f -z
where -f and -z are actually directory names
remove the q parameter to have more detail about what changed. If you want even more detail use the following
diff -rupN -- dir1 dir2

Quick and Dirty New Files Detection:
I was interested in finding the new files created after a install in a specific directory so I did the following:
Created a hardlink mirror of the directory of interest in its current structre state.
mkdir -p ~/tmp/snapshot/
cp -al ~/DirOfInterest/ ~/tmp/snapshot/
executed the install in ~/DirOfInterest/
Used the method as described in SMAs' answer
diff -qr ~/DirOfInterest/ ~/tmp/snapshot/DirOfInterest/ &>~/ListOfNewFiles.txt
Once noted the differences, I deleted the "snapshot mirror"
rm -rf ~/tmp/snapshot/DirOfInterest/
Note: Since the "snapshot directory" is a mirror made of hardlinks, modifications to already existent files at the time of "snapshooting" across the installation procedure will not be detected since the hardlinks point to the same data blobs. Only newly created Files will be detected.

Related

linux diff on folder and file structure [duplicate]

I have two directories with the same list of files. I need to compare all the files present in both the directories using the diff command. Is there a simple command line option to do it, or do I have to write a shell script to get the file listing and then iterate through them?
You can use the diff command for that:
diff -bur folder1/ folder2/
This will output a recursive diff that ignore spaces, with a unified context:
b flag means ignoring whitespace
u flag means a unified context (3 lines before and after)
r flag means recursive
If you are only interested to see the files that differ, you may use:
diff -qr dir_one dir_two | sort
Option "q" will only show the files that differ but not the content that differ, and "sort" will arrange the output alphabetically.
Diff has an option -r which is meant to do just that.
diff -r dir1 dir2
diff can not only compare two files, it can, by using the -r option, walk entire directory trees, recursively checking differences between subdirectories and files that occur at comparable points in each tree.
$ man diff
...
-r --recursive
Recursively compare any subdirectories found.
...
Another nice option is the über-diff-tool diffoscope:
$ diffoscope a b
It can also emit diffs as JSON, html, markdown, ...
If you specifically don't want to compare contents of files and only check which one are not present in both of the directories, you can compare lists of files, generated by another command.
diff <(find DIR1 -printf '%P\n' | sort) <(find DIR2 -printf '%P\n' | sort) | grep '^[<>]'
-printf '%P\n' tells find to not prefix output paths with the root directory.
I've also added sort to make sure the order of files will be the same in both calls of find.
The grep at the end removes information about identical input lines.
If it's GNU diff then you should just be able to point it at the two directories and use the -r option.
Otherwise, try using
for i in $(\ls -d ./dir1/*); do diff ${i} dir2; done
N.B. As pointed out by Dennis in the comments section, you don't actually need to do the command substitution on the ls. I've been doing this for so long that I'm pretty much doing this on autopilot and substituting the command I need to get my list of files for comparison.
Also I forgot to add that I do '\ls' to temporarily disable my alias of ls to GNU ls so that I lose the colour formatting info from the listing returned by GNU ls.
When working with git/svn or multiple git/svn instances on disk this has been one of the most useful things for me over the past 5-10 years, that somebody might find useful:
diff -burN /path/to/directory1 /path/to/directory2 | grep +++
or:
git diff /path/to/directory1 | grep +++
It gives you a snapshot of the different files that were touched without having to "less" or "more" the output. Then you just diff on the individual files.
In practice the question often arises together with some constraints. In that case following solution template may come in handy.
cd dir1
find . \( -name '*.txt' -o -iname '*.md' \) | xargs -i diff -u '{}' 'dir2/{}'
Here is a script to show differences between files in two folders. It works recursively. Change dir1 and dir2.
(search() { for i in $1/*; do [ -f "$i" ] && (diff "$1/${i##*/}" "$2/${i##*/}" || echo "files: $1/${i##*/} $2/${i##*/}"); [ -d "$i" ] && search "$1/${i##*/}" "$2/${i##*/}"; done }; search "dir1" "dir2" )
Try this:
diff -rq /path/to/folder1 /path/to/folder2

Move files in bulk and create links in their place in the directory in linux

I am trying to move hundreds of files from one directory to another but create a softlink in the old directory while doing that. Is there a single line command that can do that?
/dir1
file1.txt
file2.txt
.
.
.
file100.txt
move to dir2 and create soft link to them in dir1.
I am currently doing that seperately but was hoping to find a single line command if possible.
cd dir1
mv *.txt /dir2
ln -s /dir2/*.txt .
I tried using find but that didn't work either.
There's no single line command. It's quite trivial to do with shell scripting. For example, in tcsh:
% cd dir1
% foreach FILETOMOVE ( file*.txt )
echo mv -iv $FILETOMOVE /dir2
echo ln -s /dir2/$FILETOMOVE .
end
(Remove the echo's once you're sure you've got it right.)
Bash is similar, with slightly different syntax.
This is slightly more complicated if the filenames or paths include spaces, but still quite simple. (:q in tcsh, using "", etc.)

How to move all files including hidden files into parent directory via *

Its must be a popular question but I could not find an answer.
How to move all files via * including hidden files as well to parent directory like this:
mv /path/subfolder/* /path/
This will move all files to parent directory like expected but will not move hidden files. How to do that?
You can find a comprehensive set of solutions on this in UNIX & Linux's answer to How do you move all files (including hidden) from one directory to another?. It shows solutions in Bash, zsh, ksh93, standard (POSIX) sh, etc.
You can use these two commands together:
mv /path/subfolder/* /path/ # your current approach
mv /path/subfolder/.* /path/ # this one for hidden files
Or all together (thanks pfnuesel):
mv /path/subfolder/{.,}* /path/
Which expands to:
mv /path/subfolder/* /path/subfolder/.* /path/
(example: echo a{.,}b expands to a.b ab)
Note this will show a couple of warnings:
mv: cannot move ‘/path/subfolder/.’ to /path/.’: Device or resource busy
mv: cannot remove /path/subfolder/..’: Is a directory
Just ignore them: this happens because /path/subfolder/{.,}* also expands to /path/subfolder/. and /path/subfolder/.., which are the directory and the parent directory (See What do “.” and “..” mean when in a folder?).
If you want to just copy, you can use a mere:
cp -r /path/subfolder/. /path/
# ^
# note the dot!
This will copy all files, both normal and hidden ones, since /path/subfolder/. expands to "everything from this directory" (Source: How to copy with cp to include hidden files and hidden directories and their contents?)
I think this is the most elegant, as it also does not try to move ..:
mv /source/path/{.[!.],}* /destination/path
This will move all files to parent directory like expected but will
not move hidden files. How to do that?
You could turn on dotglob:
shopt -s dotglob # This would cause mv below to match hidden files
mv /path/subfolder/* /path/
In order to turn off dotglob, you'd need to say:
shopt -u dotglob
Alternative simpler solution is to use rsync utility:
sudo rsync -vuar --delete-after --dry-run path/subfolder/ path/
Note: Above command will show what is going to be changed. To execute the actual changes, remove --dry-run.
The advantage is that the original folder (subfolder) would be removed as well as part of the command, and when using mv examples here you still need to clean up your folders, not to mention additional headache to cover hidden and non-hidden files in one single pattern.
In addition rsync provides support of copying/moving files between remotes and it would make sure that files are copied exactly as they originally were (-a).
The used -u parameter would skip existing newer files, -r recurse into directories and -v would increase verbosity.
By using the find command in conjunction with the mv command, you can prevent the mv command from trying to move directories (e.g. .. and .) and subdirectories. Here's one option:
find /path/subfolder -maxdepth 1 -type f -name '*' -exec mv -n {} /path \;
There are problems with some of the other answers provided. For example, each of the following will try to move subdirectories from the source path:
1) mv /path/subfolder/* /path/ ; mv /path/subfolder/.* /path/
2) mv /path/subfolder/{.,}* /path/
3) mv /source/path/{.[!.],}* /destination/path
Also, 2) includes the . and .. files and 3) misses files like ..foobar, ...barfoo, etc.
You could use, mv /source/path/{.[!.],..?,}* /destination/path, which would include the files missed by 3), but it would still try to move subdirectories. Using the find command with the mv command as I describe above eliminates all these problems.
Let me introduce you to my friend "dotglob". It turns on and off whether or not "*" includes hidden files.
$ mkdir test
$ cd test
$ touch a b c .hidden .hi .den
$ ls -a
. .. .den .hi .hidden a b c
$ shopt -u dotglob
$ ls *
a b c
$ for i in * ; do echo I found: $i ; done
I found: a
I found: b
I found: c
$ shopt -s dotglob
$ ls *
.den .hi .hidden a b c
$ for i in * ; do echo I found: $i ; done
I found: .den
I found: .hi
I found: .hidden
I found: a
I found: b
I found: c
It defaults to "off".
$ shopt dotglob
dotglob off
It is best to turn it back on when you are done otherwise you will confuse things that assume it will be off.
My solution for this problem when I have to copy all the files (including . files) to a target directory retaining the permissions is: (overwrite if already exists)
yes | cp -rvp /source/directory /destination/directory/
yes is for automatically overwriting destination files,
r recursive,
v verbose,
p retain permissions.
Notice that the source path is not ending with a / (so all the files/directory and . files are copied)
Destination directory ends with / as we are placing contents of the source folder to destination as a whole.
Just do
for I in $(ls -A dir)
do
mv dir/$I newDir
done
Assuming you are in the subfolder
run find . -maxdepth 1 -exec mv {} .. \;

Using diff command to find difference between two directories

I wanna get changed parts in .diff files between the modifiied and the original kernel files.
I use diff -b -r -w dir1 dir2
it gives it on command line but I wanna get it in seperate diff extension files.
diff -b -r -w dir1 dir2 >> your_file_name.patch
will save your result.

How to copy a file to multiple directories using the gnu cp command

Is it possible to copy a single file to multiple directories using the cp command ?
I tried the following , which did not work:
cp file1 /foo/ /bar/
cp file1 {/foo/,/bar}
I know it's possible using a for loop, or find. But is it possible using the gnu cp command?
You can't do this with cp alone but you can combine cp with xargs:
echo dir1 dir2 dir3 | xargs -n 1 cp file1
Will copy file1 to dir1, dir2, and dir3. xargs will call cp 3 times to do this, see the man page for xargs for details.
No, cp can copy multiple sources but will only copy to a single destination. You need to arrange to invoke cp multiple times - once per destination - for what you want to do; using, as you say, a loop or some other tool.
Wildcards also work with Roberts code
echo ./fs*/* | xargs -n 1 cp test
I would use cat and tee based on the answers I saw at https://superuser.com/questions/32630/parallel-file-copy-from-single-source-to-multiple-targets instead of cp.
For example:
cat inputfile | tee outfile1 outfile2 > /dev/null
As far as I can see it you can use the following:
ls | xargs -n 1 cp -i file.dat
The -i option of cp command means that you will be asked whether to overwrite a file in the current directory with the file.dat. Though it is not a completely automatic solution it worked out for me.
These answers all seem more complicated than the obvious:
for i in /foo /bar; do cp "$file1" "$i"; done
ls -db di*/subdir | xargs -n 1 cp File
-b in case there is a space in directory name otherwise it will be broken as a different item by xargs, had this problem with the echo version
Not using cp per se, but...
This came up for me in the context of copying lots of Gopro footage off of a (slow) SD card to three (slow) USB drives. I wanted to read the data only once, because it took forever. And I wanted it recursive.
$ tar cf - src | tee >( cd dest1 ; tar xf - ) >( cd dest2 ; tar xf - ) | ( cd dest3 ; tar xf - )
(And you can add more of those >() sections if you want more outputs.)
I haven't benchmarked that, but it's definitely a lot faster than cp-in-a-loop (or a bunch of parallel cp invocations).
If you want to do it without a forked command:
tee <inputfile file2 file3 file4 ... >/dev/null
To use copying with xargs to directories using wildcards on Mac OS, the only solution that worked for me with spaces in the directory name is:
find ./fs*/* -type d -print0 | xargs -0 -n 1 cp test
Where test is the file to copy
And ./fs*/* the directories to copy to
The problem is that xargs sees spaces as a new argument, the solutions to change the delimiter character using -d or -E is unfortunately not properly working on Mac OS.
Essentially equivalent to the xargs answer, but in case you want parallel execution:
parallel -q cp file1 ::: /foo/ /bar/
So, for example, to copy file1 into all subdirectories of current folder (including recursion):
parallel -q cp file1 ::: `find -mindepth 1 -type d`
N.B.: This probably only conveys any noticeable speed gains for very specific use cases, e.g. if each target directory is a distinct disk.
It is also functionally similar to the '-P' argument for xargs.
No - you cannot.
I've found on multiple occasions that I could use this functionality so I've made my own tool to do this for me.
http://github.com/ddavison/branch
pretty simple -
branch myfile dir1 dir2 dir3
ls -d */ | xargs -iA cp file.txt A
Suppose you want to copy fileName.txt to all sub-directories within present working directory.
Get all sub-directories names through ls and save them to some temporary file say, allFolders.txt
ls > allFolders.txt
Print the list and pass it to command xargs.
cat allFolders.txt | xargs -n 1 cp fileName.txt
Another way is to use cat and tee as follows:
cat <source file> | tee <destination file 1> | tee <destination file 2> [...] > <last destination file>
I think this would be pretty inefficient though, since the job would be split among several processes (one per destination) and the hard drive would be writing several files at once over different parts of the platter. However if you wanted to write a file out to several different drives, this method would probably be pretty efficient (as all copies could happen concurrently).
Using a bash script
DESTINATIONPATH[0]="xxx/yyy"
DESTINATIONPATH[1]="aaa/bbb"
..
DESTINATIONPATH[5]="MainLine/USER"
NumberOfDestinations=6
for (( i=0; i<NumberOfDestinations; i++))
do
cp SourcePath/fileName.ext ${DESTINATIONPATH[$i]}
done
exit
if you want to copy multiple folders to multiple folders one can do something like this:
echo dir1 dir2 dir3 | xargs -n 1 cp -r /path/toyourdir/{subdir1,subdir2,subdir3}
If all your target directories match a path expression — like they're all subdirectories of path/to — then just use find in combination with cp like this:
find ./path/to/* -type d -exec cp [file name] {} \;
That's it.
If you need to be specific on into which folders to copy the file you can combine find with one or more greps. For example to replace any occurences of favicon.ico in any subfolder you can use:
find . | grep favicon\.ico | xargs -n 1 cp -f /root/favicon.ico
This will copy to the immediate sub-directories, if you want to go deeper, adjust the -maxdepth parameter.
find . -mindepth 1 -maxdepth 1 -type d| xargs -n 1 cp -i index.html
If you don't want to copy to all directories, hopefully you can filter the directories you are not interested in. Example copying to all folders starting with a
find . -mindepth 1 -maxdepth 1 -type d| grep \/a |xargs -n 1 cp -i index.html
If copying to a arbitrary/disjoint set of directories you'll need Robert Gamble's suggestion.
I like to copy a file into multiple directories as such:
cp file1 /foo/; cp file1 /bar/; cp file1 /foo2/; cp file1 /bar2/
And copying a directory into other directories:
cp -r dir1/ /foo/; cp -r dir1/ /bar/; cp -r dir1/ /foo2/; cp -r dir1/ /bar2/
I know it's like issuing several commands, but it works well for me when I want to type 1 line and walk away for a while.
For example if you are in the parent directory of you destination folders you can do:
for i in $(ls); do cp sourcefile $i; done

Resources