sort files by depth (bash)

后端 未结 5 2170
半阙折子戏
半阙折子戏 2020-12-19 13:39

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

相关标签:
5条回答
  • 2020-12-19 14:18

    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.

    0 讨论(0)
  • 2020-12-19 14:21

    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.

    0 讨论(0)
  • 2020-12-19 14:24

    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).

    0 讨论(0)
  • 2020-12-19 14:35

    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
    
    0 讨论(0)
  • 2020-12-19 14:36

    Use the find command:

    find . -depth
    

    From man find:

    -depth Process each directory's contents before the directory itself.

    0 讨论(0)
提交回复
热议问题