Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I am trying to recursively loop through a directory which contains numerous other directories and files and then find all the .rb files and copy them to another directory.

This is what I have so far but it is not working quite right as it copies everything in the current directory to the specified directory and does not only print .rb (ruby) files to the directory from the directory that is recursively iterated through.

This is the BASH command I am using: ls -R metasploit-framework-master | grep .rb | cp * gems

In this command metasploit-framework-master is the top-level directory to recursively iterate through with the ls -R command. Also gems is the directory to place all the .rb ruby files in.

The first part ls -R metasploit-framework-master | grep .rb does seem to find all the ruby script source files but the last part grep .rb | cp * gems does not seem to pipe in the output of the grep command to the copy command.

Can any BASH experts please help me out? `

share|improve this question

migrated from stackoverflow.com Jul 7 at 6:30

This question came from our site for professional and enthusiast programmers.

"cp * gems" does not read from stdin. –  asjo Jul 6 at 15:37
This question has nothing to do with programming. –  asjo Jul 6 at 19:04
@asjo this is not shell programming? –  user3808269 Jul 6 at 19:18
I wouldn't call piping output from one command into another programming, no - I think this question belongs on another of the sites. –  asjo Jul 6 at 20:24

2 Answers 2

up vote 2 down vote accepted

You should use find with exec, see http://en.wikipedia.org/wiki/Find#Execute_an_action . In your case, this should do it:

find metasploit-framework-master -name '*.rb' -exec cp -av {} gems \;
share|improve this answer
I tried this seems to work! :D Thank-you for the help. I am going to experiment with the other guys ideas/script first before voting an official answer. This is awesome though. :D !!! –  user3808269 Jul 6 at 15:41
what is the backslash at the end doing? or the brackets for that matter? 0_o –  user3808269 Jul 6 at 15:43
@user3808269 man find and also tons of good examples, -exec specifically. –  Jason C Jul 6 at 15:49
@JasonC thanx for the helpful info I will have to check these out. :D –  user3808269 Jul 6 at 15:50

The easiest thing to drop in here is xargs. xargs lets you take stdin and use them as arguments in other commands. I would try something like this:

## This will not work as expected ## 
$ ls -R metasploit-framework-master | grep .rb | xargs -I{} cp {} gems

However, this really won't work as you want:

  • ls -R is not going to give you the full path.
  • grep .rb will match more than just that end with .rb and should be escaped.
  • cp {} gems will not handle spaces in the path.

The better way to do this is to use find.

$ find metasploit-framework-master -name '*.rb' | xargs -I{} cp {} gems/

will let you do the recursive enumeration you want. From there you can pipe that to xargs, or even more succinct use the -exec flag:

$ find metasploit-framework-master -name '*.rb' -exec cp {} gems/ \;
share|improve this answer
ls -R does not output the file path, this is not going to work. –  Jakob Jul 6 at 15:35
@Michael Thanks, for the helpful reply. What is the brackets doing in this command (The last one) that follow the -r (recursive copy)? Also what is the backslash doing at the end and is the semi-colon actually necessary? –  user3808269 Jul 6 at 15:44
The brackets are a placeholder for where you want to fill in the argument and the \ is necessary to escape the semi-colon from the rest of the command. –  Michael Jul 6 at 15:46
Your ls answer is flawed in many ways. Beyond the point made above by @Jakob, grep .rb will match such filenames as “carbon” and “orbital”, because the “.” isn’t escaped and the regular expression isn’t anchored with a “$”. Also, find … | xargs … has a problem with filenames that contain whitespace and/or quotes; use find … -print0 | xargs -0 … instead. Also, why did you change cp to cp -r? –  Scott Jul 7 at 15:58
Generally speaking, yes use teh "xargs". I see people using find with exec. I still prefer using xargs. Also, when you do this, please use the "echo" command first whenever possible so you can see what will actually happen before it happens. I have started using for loops instead of pipe and xargs because it gives me a lot more control over using "echo" to see what's going to happen. A simple example would be like "for file in [your find statement]; do echo "cp $file gems/"; done" and then if you like what you see, take out the echo and then run it. –  ben Jul 7 at 18:43

Your Answer


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