Take the 2-minute tour ×
Server Fault is a question and answer site for system and network administrators. It's 100% free, no registration required.

I am interested in recursively grepping for a string in a directory for a pattern and then cp'ing the matching file to a destination directory. I thought I could do something like the following, but it doesn't seem to make sense with linux's find tool:

find . -type f -exec grep -ilR "MY PATTERN" | xargs cp /dest/dir

Is there a better way of going about this? Or a way that even works would be a solid start.

share|improve this question

4 Answers 4

up vote 3 down vote accepted

Do man xargs and look at the -I flag.

find . -type f -exec grep -ilR "MY PATTERN" {} \; | xargs -I % cp % /dest/dir/

Also, find expects a \; or + after the -exec flag.

share|improve this answer
    
Forgot to mention that I'm getting the error find: missing argument to -exec` –  imaginative Sep 13 '13 at 0:15
    
Updated answer. –  Sammitch Sep 13 '13 at 0:24

xargs reads its standard input and puts them at the end of the command to be executed. As you've written it, you would end up executing

cp /dest/dir sourcefile1 sourcefile2 sourcefile3

which is backwards from what you want.

You could use the -I option to specify a placeholder for xargs, like this: xargs -I '{}' cp '{}' /dest/dir.

Also, find takes care of the recursion, so your grep does not need -R.

Final solution:

find . -type f -exec grep -il "MY PATTERN" '{}' + | xargs -I '{}' cp '{}' /dest/dir
share|improve this answer
    
I commented below also, does -exec need to be terminated? I'm getting find: missing argument to -exec, not sure where I'd put {} \;` when there is a pipe to xargs involved. –  imaginative Sep 13 '13 at 0:22
    
Thanks, corrected. I've used + instead of \; for efficiency, since GNU find supports it. –  200_success Sep 13 '13 at 1:31
    
@imaginative There is no fixed "above" and "below" on Stack Exchange; the relative position of posts change both because of voting, activity and user preference (see the "active", "oldest", "votes" sorting tabs just below the question). Hence, it is best to be explicit about which answer you refer to (by linking or at least specifying the full name of the answerer works well in my experience). For example, for me the only "below" currently is Neil H Watson's unvoted and uncommented answer. I'm guessing you are referring to Sammitch's answer, which is now on top. –  Michael Kjörling Sep 13 '13 at 9:23

None of the examples above takes into account the possibility of grep's -l generating duplicated results (files with the same name, but in different places), and therefore find's exec's hook overwriting files in the destination directory.

An attempt to solve this:

$ tree foo
foo
├── bar
│   ├── baz
│   │   └── file
│   └── file
├── destdir
└── file

3 directories, 3 files

$ while read a; do mv $a foo/destdir/$(basename $a).$RANDOM; done < <(grep -rl content foo)

$ tree foo
foo
├── bar
│   └── baz
└── destdir
    ├── file.10171
    ├── file.10842
    └── file.25404

3 directories, 3 files
share|improve this answer

I don't think you need find. Recursive grep:

grep -rl "MY PATTERN" .
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.