How to go to each directory and execute a command?

BashShellUnixFind

Bash Problem Overview


How do I write a bash script that goes through each directory inside a parent_directory and executes a command in each directory.

The directory structure is as follows: > parent_directory (name could be anything - doesnt follow a pattern) > > - 001 (directory names follow this pattern) > - 0001.txt (filenames follow this pattern) > - 0002.txt > - 0003.txt > - 002 > - 0001.txt > - 0002.txt > - 0003.txt > - 0004.txt > - 003 > - 0001.txt > > the number of directories is unknown.

Bash Solutions


Solution 1 - Bash

This answer posted by Todd helped me.

find . -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd" \;

The \( ! -name . \) avoids executing the command in current directory.

Solution 2 - Bash

You can do the following, when your current directory is parent_directory:

for d in [0-9][0-9][0-9]
do
    ( cd "$d" && your-command-here )
done

The ( and ) create a subshell, so the current directory isn't changed in the main script.

Solution 3 - Bash

If you're using GNU find, you can try -execdir parameter, e.g.:

find . -type d -execdir realpath "{}" ';'

or (as per @gniourf_gniourf comment):

find . -type d -execdir sh -c 'printf "%s/%s\n" "$PWD" "$0"' {} \;

Note: You can use ${0#./} instead of $0 to fix ./ in the front.

or more practical example:

find . -name .git -type d -execdir git pull -v ';'

If you want to include the current directory, it's even simpler by using -exec:

find . -type d -exec sh -c 'cd -P -- "{}" && pwd -P' \;

or using xargs:

find . -type d -print0 | xargs -0 -L1 sh -c 'cd "$0" && pwd && echo Do stuff'

Or similar example suggested by @gniourf_gniourf:

find . -type d -print0 | while IFS= read -r -d '' file; do
# ...
done

The above examples support directories with spaces in their name.


Or by assigning into bash array:

dirs=($(find . -type d))
for dir in "${dirs[@]}"; do
  cd "$dir"
  echo $PWD
done

Change . to your specific folder name. If you don't need to run recursively, you can use: dirs=(*) instead. The above example doesn't support directories with spaces in the name.

So as @gniourf_gniourf suggested, the only proper way to put the output of find in an array without using an explicit loop will be available in Bash 4.4 with:

mapfile -t -d '' dirs < <(find . -type d -print0)

Or not a recommended way (which involves parsing of ls):

ls -d */ | awk '{print $NF}' | xargs -n1 sh -c 'cd $0 && pwd && echo Do stuff'

The above example would ignore the current dir (as requested by OP), but it'll break on names with the spaces.

See also:

Solution 4 - Bash

You can achieve this by piping and then using xargs. The catch is you need to use the -I flag which will replace the substring in your bash command with the substring passed by each of the xargs.

ls -d */ | xargs -I {} bash -c "cd '{}' && pwd"

You may want to replace pwd with whatever command you want to execute in each directory.

Solution 5 - Bash

If the toplevel folder is known you can just write something like this:

for dir in `ls $YOUR_TOP_LEVEL_FOLDER`;
do
    for subdir in `ls $YOUR_TOP_LEVEL_FOLDER/$dir`;
    do
      $(PLAY AS MUCH AS YOU WANT);
    done
done

On the $(PLAY AS MUCH AS YOU WANT); you can put as much code as you want.

Note that I didn't "cd" on any directory.

Cheers,

Solution 6 - Bash

for dir in PARENT/*
do
test -d "$dir" || continue



Do something with $dir...



done

done

Solution 7 - Bash

While one liners are good for quick and dirty usage, I prefer below more verbose version for writing scripts. This is the template I use which takes care of many edge cases and allows you to write more complex code to execute on a folder. You can write your bash code in the function dir_command. Below, dir_coomand implements tagging each repository in git as an example. Rest of the script calls dir_command for each folder in directory. The example of iterating through only given set of folder is also include.

#!/bin/bash

#Use set -x if you want to echo each command while getting executed
#set -x

#Save current directory so we can restore it later
cur=$PWD
#Save command line arguments so functions can access it
args=("$@")

#Put your code in this function
#To access command line arguments use syntax ${args[1]} etc
function dir_command {
    #This example command implements doing git status for folder
	cd $1
	echo "$(tput setaf 2)$1$(tput sgr 0)"
	git tag -a ${args[0]} -m "${args[1]}"
    git push --tags
	cd ..
}

#This loop will go to each immediate child and execute dir_command
find . -maxdepth 1 -type d \( ! -name . \) | while read dir; do
   dir_command "$dir/"
done

#This example loop only loops through give set of folders    
declare -a dirs=("dir1" "dir2" "dir3")
for dir in "${dirs[@]}"; do
    dir_command "$dir/"
done

#Restore the folder
cd "$cur"

Solution 8 - Bash

I don't get the point with the formating of the file, since you only want to iterate through folders... Are you looking for something like this?

cd parent
find . -type d | while read d; do
   ls $d/
done

Solution 9 - Bash

you can use

find .

to search all files/dirs in the current directory recurive

Than you can pipe the output the xargs command like so

find . | xargs 'command here'

Solution 10 - Bash

  #!/bin.bash
for folder_to_go in $(find . -mindepth 1 -maxdepth 1 -type d \( -name "*" \) ) ; 
									# you can add pattern insted of * , here it goes to any folder 
									#-mindepth / maxdepth 1 means one folder depth   
do
cd $folder_to_go
  echo $folder_to_go "########################################## "
  
  whatever you want to do is here

cd ../ # if maxdepth/mindepath = 2,  cd ../../
done

#you can try adding many internal for loops with many patterns, this will sneak anywhere you want

Solution 11 - Bash

You could run sequence of commands in each folder in 1 line like:

for d in PARENT_FOLDER/*; do (cd "$d" && tar -cvzf $d.tar.gz *.*)); done

Solution 12 - Bash

for p in [0-9][0-9][0-9];do
    (
        cd $p
        for f in [0-9][0-9][0-9][0-9]*.txt;do
            ls $f; # Your operands
        done
    )
done

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionVan de GraffView Question on Stackoverflow
Solution 1 - BashChristian VielmaView Answer on Stackoverflow
Solution 2 - BashMark LongairView Answer on Stackoverflow
Solution 3 - BashkenorbView Answer on Stackoverflow
Solution 4 - BashPiyush SinghView Answer on Stackoverflow
Solution 5 - BashgforcadaView Answer on Stackoverflow
Solution 6 - BashIdelicView Answer on Stackoverflow
Solution 7 - BashShital ShahView Answer on Stackoverflow
Solution 8 - BashAifView Answer on Stackoverflow
Solution 9 - BashDan BizdadeaView Answer on Stackoverflow
Solution 10 - BashHutchView Answer on Stackoverflow
Solution 11 - Bashuser1859675View Answer on Stackoverflow
Solution 12 - BashFedir RYKHTIKView Answer on Stackoverflow