Is there a way in bash to sort my files from a directory down in depth order, for example first print the files in the present directory, then the files in the sub directory
Use find's "-printf" feature in combination with sort. See for yourself:
find . -printf "%d %p\n"|sort -n
It generates a depth-sorted list (displaying the depth in the first column, file path in the second). This prints in my current dir:
0 .
1 ./bin
1 ./log
1 ./templates
2 ./bin/cc_env
3 ./files/test/mail.txt
If you want to strip the first column, we can use perl:
find . -printf "%d %p\n"|sort -n|perl -pe 's/^\d+\s//;'
and off you go. The perl filter will remove all leading numbers. In case you want to omit directories themselves, use the '-type f' parameter:
find . -type f -printf "%d %p\n"|sort -n|perl -pe 's/^\d+\s//;'
Hint: Study the find manpage for more printf %d-like tricks.
A plausible technique would use find
to generate the file pathnames, and then process the file names so that a count of the number of slashes in the path precedes the name, and then you can sort by the count of slashes (depth) and then by name. The simple solutions assume that there are no newlines in your file names (other spaces and odd-ball characters do not matter). The interesting/tricky part is finding a clean way of counting the number of slashes in a given line.
find . -type f -print |
perl -n -e '$x = $_; $x =~ tr%/%%cd; print length($x), " $_";' |
sort -k 1n -k 2 |
sed 's/^[0-9][0-9]* //'
There's probably a more compact way of writing that Perl, but that works.
The simplest solution:
$ echo * */* */*/* */*/*/* */*/*/*/* */*/*/*/*/*
a a/b a/b/c a/b/c/d1 a/b/c/d2 a/b/c/d1/e a/b/c/d2/e a/b/c/d1/e/f a/b/c/d2/e/f
Or, in column:
$ echo * */* */*/* */*/*/* */*/*/*/* */*/*/*/*/* |tr ' ' '\n'
a
a/b
a/b/c
a/b/c/d1
a/b/c/d2
a/b/c/d1/e
a/b/c/d2/e
a/b/c/d1/e/f
a/b/c/d2/e/f
The depth of the tree is hardcoded in the example, but you can write a small script and make it more flexible:
A="*"
while true
do
B=$(echo $A)
[ "$B" = "$A" ] && break
echo $B
A="$A/*"
done | tr ' ' '\n'
Example of usage:
$ A="*"; while true; do B=$(echo $A); [ "$B" = "$A" ] && break; echo $B; A="$A/*"; done | tr ' ' '\n'
a
a/b
a/b/c
a/b/c/d1
a/b/c/d2
a/b/c/d1/e
a/b/c/d2/e
a/b/c/d1/e/f
a/b/c/d2/e/f
Examples are provided for the tree:
$ mkdir -p a/b/c/d{1,2}/e/f
$ tree .
.
└── a
└── b
└── c
├── d1
│ └── e
│ └── f
└── d2
└── e
└── f
9 directories, 0 files
Find/depth solutions will obviously not work because find
will shows subtrees one after another. The -depth
key says in which direction subtree must be shown. But that doesn't mean, of course, that the output will be sorted by depth.
$ find .
.
./a
./a/b
./a/b/c
./a/b/c/d2
./a/b/c/d2/e
./a/b/c/d2/e/f
./a/b/c/d1
./a/b/c/d1/e
./a/b/c/d1/e/f
$ find . -depth
./a/b/c/d2/e/f
./a/b/c/d2/e
./a/b/c/d2
./a/b/c/d1/e/f
./a/b/c/d1/e
./a/b/c/d1
./a/b/c
./a/b
./a
.
As you can see the answer is incorrect in both cases (with and without -find
).
use a function to recursively traverse the filesystem
test.sh:
#!/bin/bash
function traverse() {
find $1 -mindepth 1 -maxdepth 1 ! -type d -exec echo "$2"{} \;
for d in $(find $1 -mindepth 1 -maxdepth 1 -type d ! -name ".")
do
# if you just need files comment out next line
echo "$2$d"
traverse "$d" "${2} "
done
}
traverse $1
usage:
./test.sh dir
gives output:
./test.sh
./testA
./testA/dat2
./testA/dat1
./testA/testB
./testA/testB/dat4
./testC
./testC/dat3
Use the find
command:
find . -depth
From man find
:
-depth Process each directory's contents before the directory itself.