Loop through directories and subdirectories in bash
I tried with this line, starting at the parent folder containing the folders I want to loop through:
for dir in */; do
cd $dir
for dir2 in */; do
cd $dir2
ls -d $PWD/*
cd ..
done
cd ..
done
But it just outputs the whole contents of my whole computer and goes back to root folder /~ ... any explanation for this behavior?
bash
's default "globbing" (wildcard matching/expansion) behavior is to return the wildcard expression itself if the wildcard doesn't match anything. So if you cd
into a directory that has no subdirectories, you'll end up trying to cd
into literally '*/' (literally a directory whose name is an asterisk), and that will fail, so you'll stay one directory higher than you thought you'd be at this point in the loop. So when you then cd ..
, you'll be one directory higher again. Eventually you'll back yourself out to the root level of your filesystem.
Many shell scripters choose to use find(1)
rather than shell loops when they want to have a script walk a directory tree. Your whole script snippet would become:
find . -type d
…or, if you really wanted absolute paths rather than relative paths:
find "$PWD" -type d
If you want to stick with shell loops for this, you might try something like this:
#!/bin/bash
set -evx
for dir in */; do
if [ -d "$dir" ]; then
cd "$dir"
for dir2 in */; do
if [ -d "$dir2" ]; then
cd "$dir2"
ls -d "$PWD"/*
cd ..
fi
done
cd ..
fi
done
Note how I've used if
statements to test each value of dir
and dir2
to make sure they are truly directories, and I skip them if they are not.
Also note the set -evx
line, which are a nice set of options to set when you're first writing/debugging a script:-e
stops on the first error.-v
prints each line before it executes it.-x
prints each line after all the various substitutions/expansions have been performed, before executing it.
Also consider spending some time in the bash(1)
man page looking at shell options like nullglob
and failglob
, if you don't like the default globbing behavior of returning the wildcard expression itself when it fails to match anything.
You can simply use multiple asterixes:
for dir in */*; do
ls -d $PWD/* &
done
Use the &
to run the code parallelised.