LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Newbie (http://www.linuxquestions.org/questions/linux-newbie-8/)
-   -   Executing a script in multiple subdirectories simultaneously (http://www.linuxquestions.org/questions/linux-newbie-8/executing-a-script-in-multiple-subdirectories-simultaneously-832102/)

jhavird 09-14-2010 10:51 AM

Executing a script in multiple subdirectories simultaneously
 
Hi,

I'm a newbie to shell scripting and have come up with a script (named "drop_3rd.sh") that I'd like to execute in several subdirectories that all have the same name, but I don't want to have to change into each subdirectory (there are hundreds) to execute the script.

All the subdirectories have the same name ("final_alignments"), but are located in parent directories with different names. All these parent directories are in the same "super parent" directory.

I've tried to do this using "for/do" and "find" but no luck so far.

Please help, as I'm sure this is a straightforward problem that can easily be solved by someone with more experience.

Thanks!

GrapefruiTgirl 09-14-2010 11:03 AM

I believe you're on the right track using `find` and a for/do loop. It'd be a good idea to show us what is the exact code you're currently using so that we might point out what you're doing wrong. Anyhow, here's the basic idea I'd use:

Code:

for location in $(find /path/to/super_parent_directory -type d -name "final_alignments"); do

 ( cd "$location"
  /path/to/script )

done;

So, it uses `find` to recursively search within the /path/to/super_parent_directory and locate directories (that's what the -type d is for) and it then (in a subshell) cd's into each final_alignments directory that it finds, and executes the script.

If this doesn't work, please show us (copy & paste) from your terminal how you've used this code, and what the results and error messages are.

Good luck!

dannybpng 09-14-2010 04:50 PM

Let find do it all
 
Find can locate the directories, cd to each one, and run a command like this.

find /path/to/super_parent_directory -type d -name "final_alignments" -execdir /path/to/script \;

The \; is important as it keeps the shell from interpreting it and passes it on to the find command.

GrapefruiTgirl 09-14-2010 05:14 PM

@ danny,

thanks, that's something I hadn't thought of!

jhavird 09-15-2010 08:54 AM

Thanks!
 
Both these solutions seem to work, but I went with Danny's to save some time.

Thanks!

:)

Chirel 09-15-2010 01:37 PM

You choosed the right one, because the first don't work if there is spaces on dir names.

dannybpng 09-15-2010 02:40 PM

FYI
The reason is that the results of the find command are "expanded" and placed on the "for" command line before each iteration, so the spaces get parsed as separate words. Another overhead problem is that the "( command )" has to create a subshell environment for each line. The "find" version is best. Here is a way you could do it if you'd like.

find /path/to/super_parent_directory -type d -name "final_alignments" | while read location; do pushd "$location">/dev/null; /path/to/script; popd>/dev/null; done;

In this example spaces in directory names get passed on OK. You always have to be careful of spaces in shell scripts. I always enclose variables being expanded in double quotes so it is expanded as one "word". The pushd and popd commands provide for cd'ing to a directory then poping back without having to start a subshell.

Valery Reznic 09-15-2010 03:52 PM

OK, you already have a solution, but anyway.

What about:

Code:

for dir in /path_to/super_parent/*/final_alignment; do
  (
      cd $dir || exit
      /path/to/script || exit
  ) || exit
done

While find's -exec can be handy I not like it - even if one of the exec's failed, find's exit status still be 0

grail 09-15-2010 06:40 PM

You can also get around the word splitting by using a while loop instead:
Code:

while read -r line
do
    cd "$line"
    /path/to/script || exit 1
done< <(find /path/to/super_parent_directory -type d -name "final_alignments")

Please note there is a space between < < after done


All times are GMT -5. The time now is 02:56 PM.