If you are just interested in separating directories from everything else, you can do something like this, using a recursive function:
#!/bin/bash
## Do not expand globs to themselves if they don't match
shopt -s nullglob
list_files(){
## if a target has been passed as an argument, use that; if not,
## default to '.', the current directory.
target=${1:-.}
for i in "$target"/*; do
if [ -d "$i" ]; then
list_files "$i"
else
echo "$i is not a directory"
fi
done
}
list_files "$@"
Given a directory structure like this:
$ tree
.
├── 1.txt
├── 2.txt
├── 3.txt
├── dir1
│ ├── subdir1
│ │ ├── file
│ │ ├── subsubdir1
│ │ └── subsubdir2
│ └── subdir2
│ ├── file
│ ├── subsubdir1
│ └── subsubdir2
├── dir2
│ ├── subdir1
│ │ ├── subsubdir1
│ │ └── subsubdir2
│ └── subdir2
│ ├── subsubdir1
│ └── subsubdir2
└── symlink -> 2.txt
15 directories, 6 files
The code above produces:
$ bar.sh
"./1.txt" is not a directory
"./2.txt" is not a directory
"./3.txt" is not a directory
"./dir1" is a directory
"./dir1/subdir1" is a directory
"./dir1/subdir1/file" is not a directory
"./dir1/subdir1/subsubdir1" is a directory
"./dir1/subdir1/subsubdir2" is a directory
"./dir1/subdir2" is a directory
"./dir1/subdir2/file" is not a directory
"./dir1/subdir2/subsubdir1" is a directory
"./dir1/subdir2/subsubdir2" is a directory
"./dir2" is a directory
"./dir2/subdir1" is a directory
"./dir2/subdir1/subsubdir1" is a directory
"./dir2/subdir1/subsubdir2" is a directory
"./dir2/subdir2" is a directory
"./dir2/subdir2/subsubdir1" is a directory
"./dir2/subdir2/subsubdir2" is a directory
"./symlink" is not a directory
You can also make it a little easier to read by adding an offset to indicate the depth in the directory tree, and by processing all directories first so as to have a more ordered output:
#!/bin/bash
## Do not expand globs to themselves if they don't match
shopt -s nullglob
list_files(){
## if a target has been passed as an argument, use that; if not,
## default to '.', the current directory.
target=${1:-.}
for i in "$target"/*; do
offset=${2:-""}
if [ -d "$i" ]; then
printf '%s"%s" is a directory\n' "$offset" "$i"
for subdir in "$i"/*/; do
new_offset="$offset "
list_files "$i" "$new_offset"
new_offset=""
done
else
printf '%s"%s" is not a directory\n' "$offset" "$i"
fi
done
}
list_files "$@"
This will produce the following output on the example input above:
$ foo.sh
"./1.txt" is not a directory
"./2.txt" is not a directory
"./3.txt" is not a directory
"./dir1" is a directory
"./dir1/subdir1" is a directory
"./dir1/subdir1/file" is not a directory
"./dir1/subdir1/subsubdir1" is a directory
"./dir1/subdir1/subsubdir2" is a directory
"./dir1/subdir1/subsubdir2/file" is not a directory
"./dir1/subdir2" is a directory
"./dir1/subdir2/file" is not a directory
"./dir1/subdir2/subsubdir1" is a directory
"./dir1/subdir2/subsubdir2" is a directory
"./dir2" is a directory
"./dir2/subdir1" is a directory
"./dir2/subdir1/subsubdir1" is a directory
"./dir2/subdir1/subsubdir2" is a directory
"./dir2/subdir2" is a directory
"./dir2/subdir2/subsubdir1" is a directory
"./dir2/subdir2/subsubdir2" is a directory
"./symlink" is not a directory
Note that this approach will descend into symlinked directories. For example, if I add this:
$ ln -s dir2 dirsym
$ ls -ld dirsym
lrwxrwxrwx 1 terdon terdon 4 Apr 8 16:03 dirsym -> dir2
Then dirsym
will be treated as a directory and its contents will be reported just like its target, dir2
, effectively repeating the dir2
output. I don't know if this is the desired behavior or not. If it isn't, use caz's find
approach instead.
'$FILE'
evaluates to the string$FILE
and not to the file name. (and assuming you have nothing that is called$FILE
in your directory, this nonexistent filesystem entry is of course always not a directory)for
loop inside the original loop?