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?".