Recursive find and copy to other directory - linux

I need to find all files in a directory and it's subdirectories, but I need to keep directory structure. For example there is a file
/media/subdir1/subdir2/file.jpg
and I want to copy it to
/new-media/subdir1/subdir2/file.jpg
and the same to all files inside /media/ directory. And by the way, directories inside /new-media/ must be created if not exist.
if I use
find /media/ -name '*.jpg' -exec cp /new-media/ ????? {} \;
how can I get all subdirectories inside /media/?

The above will get you everything in /media, but to get exactly what you want you probably want to use something like:
Method 1: Copy only what you need recursively, per your requirements:
mkdir ../media2; find . -name "*.jpg" -exec cp -r --parents {} ../media2 \;
I ran this from inside the directory you want to search recursively. It does a couple things:
1 - create the new destination directory
mkdir ../media2
2 - then finds all files ending with ".jpg" recursively.
find . -name "*.jpg"
3 - uses -exec to pass the copy command to each file returned to find as a match, and subs that in as the first argument (which with the syntax of cp, is going to be your source file):
-exec cp -r --parents {} ../media2 \;
the "--parents" flag retains existing directory structure and recursively creates subsequent parent directories. Super useful right?
Method 2: there might be a better way to do this with xargs, but the above ended up being the most simple method imho. That said, if you want to think outside the box, you could simply copy the entire directory over recursively, then remove anything NOT ending with ".jpg" with something like:
cp -r media media2; find ./media '!'-name "*.jpg" -type f | xargs rm

I think this is what you want:
cp -r /media /new-media
-R, -r, --recursive
copy directories recursively
Hope this helps.

Related

Linux move files from dir to dir by name mask

I am trying to move all files with names starts with SML from directory to another.
Tried with
find /var/.../Images/ -name SML\* mv /var/.../Images/Small but doesnt work
try find /var/.../Images/ -name SML\* -exec mv {} /var/.../Images/Small/ \;
I guess you want something like this:
dir=/path/to/your/Images
mkdir -p "$dir/Small"
find "$dir" -name "SML*" -not -wholename "$dir/Small/*" -exec mv {} "$dir/Small/" \;
Since the directory you move the files to is a subdirectory of the one you seach in, you need to exclude the files already moved there. So I added -not -wholename "$dir/Small/*"
To execute a command for each found file, you need -exec .... The alternative would be to pipe your find results to a while read loop.
When using -exec, the found name can be referenced by {}.
See man find for a lot more information.

Newbie-ish error: cp: omitting directory

Pulling my hair as I'm stuck with a basic error without understanding why:
find . -type f -exec cp del {} \;
We're in a "test" directory, in which I created one "del" subdirectory. The "test" directory contains a variety of files of various types.
The result is a series of lines (same number as the number of files present in the directory from where the command is ran) with:
cp: omitting directory `del'
Possibly useful details follow.
Debian Wheezy, standard shell interface.
As a prelude to more complex exclusion and exec patterns I wanted to start with this fundamental test... and had this.
I think I excluded the "del" directory with "type -f", so it's not as if I was asking Linux to move a directory within itself.
There are no other directories or subdirectories.
Permissions: everything belongs to the current user.
I made variations for the "cp del" part, putting it in simple or double quotes, or using ./del, no difference.
I also tried with -R
find . -type f -name '*script1*' -exec cp -R ./del {} \;
That gave:
cp: cannot overwrite non-directory `./script1' with directory `./del'
Same with -r
If what you're trying to do is to copy some files found by find command to the del directory, then you can do it like this:
find . -type f | xargs cp -T del/
Or like this:
find . -type f -exec cp {} del \;

Linux: copy ".svn" directories recursively

I know there are dozen of questions about similar topcis but I still can't beat this up.
I need to copy all .svn directories recursively from /var/foo to /var/foo2 on a Debian machine:
/var/www/foo/.svn
/var/www/foo/bar/.svn
...
I tried these two commands without success:
find /var/foo -name ".svn" -type f -exec cp {} ./var/foo2 \;
find /var/foo -name ".svn" -type d -exec cp {} /var/foo2 \;
Once only the svn directory right inside foo is copied, while another time nothing is copied.
Given following file structure:
./
./a/
./a/test/
./a/test/2
./b/
./b/3
./test/
./test/1
Running following script in the directory to be copied:
find -type d -iname test -exec sh -c 'mkdir -p "$(dirname ~/tmp2/{})"; cp -r {}/ ~/tmp2/{}' \;
Should copy all test directories to ~/tmp2/.
Points of interest:
Directories are copied to the destination on a one-by-one basis
Parent directories are created in advance so that cp doesn't complain about target not existing
Rather than just cp, cp -r is used
The whole command is wrapped with sh -c so that operations on {} such as dirname can be performed (so that the shell expands it for each directory separately, rather than expanding it once during calling the find)
Resulting structure in ~/tmp2:
./
./a/
./a/test/
./a/test/2
./test/
./test/1
So all you should need to do is to replace test with .svn and ~/tmp2 with directory of choice. Just remember about running it in the source directory, instead of using absolute paths.
I find that using tar for such operations makes the code often much more readable:
$ mkdir /var/www/foo2
$ cd /var/www/foo2
$ find ../foo/ -type d -name .svn -exec tar c \{\} \+ | \
tar x --strip-components=1
find will list all directories named .svn, and call tar to create (c) an archive file (that is sent to stdout) with all these directories. the archive on stdout is then extracted (x) by another tar instance in the target directory. the relative path portion (../) is automatically removed by the archiving tar, but since we also want to remove the first path component (foo/) we need to add --strip-components.
Note: This will only work if you do not have very many .svn directories you want to copy (more than $(getconf ARG_MAX)-2, which on my system is more than 200000).

Howto replace a file in several sub-folders

I've a series of directories containing a set of files. There is a new copy of this file which I would like to replace all instances with. How can do this with find command?
Latest file is in /var/www/html is called update_user.php
There are 125 directories with several other files including a copy of update_user.php. I want to replace these with the one in update_user.php excluding itself.
This should do the job:
find /path/to/old/files -type f -name update_user.php -exec cp /path/to/new/update_user.php {} \;
You should check if the new file is not inside /path/to/old and if so than first copy it outside and use that copy but.. it'll not harm if you don't - one cp will fail with are the same file error.
You can use
cp -v to see what it does
cp -u to update only when source file is newer
echo cp to perform dry run
I would suggest to check first if all dest. files are the same with:
find /path/to/old/files -type f -name update_user.php -exec md5sum {} \;|awk '{print $1}'|sort|uniq

Backup files with dir structure bash script

I'm making a bash script that should backup all files and dir structure to another dir.
I made the following code to do that:
find . -type f -exec cp {} $HOME/$bdir \; -o -type d -exec mkdir -p {} $HOME/$bdir \; ;
The problem is, is that this only copies the files and not the dir structure.
NOTE: I may not use cp -r, cp -R or something like it because this code is part of an assignment.
I hope somebody can put me in the right direction. ;)
Joeri
EDIT:
I changed it to:
find . -type d -exec mkdir -p $HOME/$bdir/{} \; ;
find . -type f -exec cp {} $HOME/$bdir/{} \; ;
And it works! Ty guys ;)
This sounds like a job for rsync.
You mention that this is an assignment. What are your restrictions? Are you limited to only using find? Does it have to be a single command?
One way to do this is to do it in two find calls. The first call only looks for directories. When a directory is found, mkdir the corresponding directory in the destination hierarchy. The second find call would look for files, and would use a cp command like you currently have.
You can also take each filename, transform the path manually, and use that with the cp command. Here's an example of how to generate the destination filename:
> find . -type f | sed -e "s|^\./|/new/dir/|"
/new/dir/file1.txt
/new/dir/file2.txt
/new/dir/dir1/file1_1.txt
/new/dir/dir1/file1_2.txt
For your purposes, you could write a short bash script that take the source file as input, uses sed to generate the destination filename, and then passes those two paths to cp. The dirname command will return the directory portion of a filename, so mkdir -p $(dirname $destination_path) will ensure that the destination directory exists before you call cp. Armed with a script like that, you can simply have find execute the script for every file it finds.
cd olddir; tar c . | (cd newdir; tar xp)
Can you do your find with "-type d" and exec a "mkdir -p" first, followed by your find that copies the files rather than having it all in one command? It should probably also be mkdir -p $HOME/$bdir/{}.

Resources