Your function parses the unquoted output of ls
. This means that the shell will perform word splitting (by default on any whitespace) and filename generation (globbing) on the output of ls
.
Instead:
dirwalk () {
indent=${1:-0}
for name in *; do
[ ! -e "$name" ] && continue
if [ -d "$name" ]; then
printf '%*sDir: "%s"\n' "$indent" "" "$name"
( cd "$name" && dirwalk "$(( indent + 4 ))" )
else
printf '%*sFile: "%s"\n' "$indent" "" "$name"
fi
done
}
or, without the fancy indentation,
dirwalk () {
for name in *; do
[ ! -e "$name" ] && continue
if [ -d "$name" ]; then
printf 'Dir: "%s"\n' "$name"
( cd "$name" && dirwalk )
else
printf 'File: "%s"\n' "$name"
fi
done
}
By using *
instead of the output of ls
, we generate a properly delimited list of filenames to iterate over in the current directory, even if these contain spaces.
The -e
test is to make sure that the thing that we iterate over actually exists. If we enter an empty directory, the *
would not expand and would remain as *
. In bash
, you may set the nullglob
shell option (with shopt -s nullglob
) to make unmatched patterns expand to nothing instead.
By using cd
in a subshell (within parentheses), we don't have to remember to cd ..
to get back to the previous directory. The environment outside of ( ... )
would not be affected by the cd
inside it.
Using printf
rather than echo
we have greater control over the formatting of the output. Also see "Why is printf better than echo?".