2

I have a text file containing a list of files, such as:

file
A1/file
A1/B1/file
A2/file

How can I sort it such that files in subdirectories are listed before other files in the parent directory?

A1/B1/file
A1/file
A2/file
file
4
  • 2
    If the file was created using find, then adjusting the arguments that were used with that command would be the easiest approach as it wouldn't require any post-processing steps. Simply adding -depth would likely be enough. This is not an answer as there is no information in the question about the origin of the data.
    – Kusalananda
    Commented May 15, 2023 at 17:07
  • If it helps, the origin of the data is output of diff on a directory (git diff to be specific), with the initial characters stripped M ./
    – Ken Taylor
    Commented May 18, 2023 at 16:09
  • git diff also produces an actual diff of the file contents, so I presume that it's not a straight git diff.
    – Kusalananda
    Commented May 18, 2023 at 17:30
  • git --no-pager diff --name-status origin/master
    – Ken Taylor
    Commented May 23, 2023 at 17:53

3 Answers 3

3

Here a simple way:

awk 'BEGIN{FS="/"; OFS="\t"}{print NF, $0}' file | sort -rn | cut -f2-

A1/B1/file
A2/file
A1/file
file

Should works for simple cases, like your question.

If you want to sort also by dirs:

awk 'BEGIN{FS="/"; OFS="\t"}{print NF, $0}' file | sort -k1,1rn -k2 | cut -f2-

A1/B1/file
A1/file
A2/file
file
6
  • awk -F/ '{printf "%d\t%s\n", NF, $0}' file | sort -k1,2rn -k2,99 | cut -f2 will sort items at the same level correctly Commented May 12, 2023 at 22:34
  • Neither of these list subdirectories before files. Maybe misleading to have asked for "depth first" when what I actually want is in the body of the question, subdirectories before files in the same directory. Neither of the solutions so far do that either. If I add A2/B1/C1/file to the directory, I want to see: A1/B1/file A1/file A2/B1/C1/file A2/file file
    – Ken Taylor
    Commented May 15, 2023 at 15:06
  • I am going to repost this question properly
    – Ken Taylor
    Commented May 15, 2023 at 15:18
  • 2
    @KenTaylor please don't. Instead, edit this one and clarify what you need.
    – terdon
    Commented May 15, 2023 at 18:01
  • @KenTaylor, "what I actually want is […] subdirectories before files in the same directory" - this isn't what your question asks Commented May 15, 2023 at 20:34
2

zsh has the od glob qualifier to order glob expansions depth-first,

$ print -rC1 -- **/file(Nod)
A2/file
A1/B1/file
A1/file
file

That can be applied to an arbitrary list of files, if that list of files is loaded into a glob expansion using the e qualifier.

$ print -rC1 -- /(Ne['reply=( ${(f)"$(<your-file)"} )']od)
A2/file
A1/B1/file
A1/file
file

Also sorted by name with on:

$ print -rC1 -- /(Ne['reply=( ${(f)"$(<your-file)"} )']odon)
A1/B1/file
A1/file
A2/file
file
0
1

To expand on the insights of @Kusalananda, if indeed these filenames are the complete contents of an extant directory tree, find can do what you want:

Given:

$ find .
./f2
./A1
./A1/B1
./A1/B1/f2
./A1/B1/f1
./A1/f2
./A1/f1
./A2
./A2/B1
./A2/B1/C1
./A2/B1/C1/f2
./A2/B1/C1/f1
./A2/f2
./A2/f1
./f1

GNU find can:

$ find . -depth -type d -exec find {} -maxdepth 1 -not -type d \;
./A1/B1/f2
./A1/B1/f1
./A1/f2
./A1/f1
./A2/B1/C1/f2
./A2/B1/C1/f1
./A2/f2
./A2/f1
./f2
./f1

If you care about the sorting of files within a directory, GNU find lets you down, having no controls over the ordering. You might have to do something like:

$ find . -depth -type d -exec sh -c 'find {} -maxdepth 1 -not -type d | sort' \;
./A1/B1/f1
./A1/B1/f2
./A1/f1
./A1/f2
./A2/B1/C1/f1
./A2/B1/C1/f2
./A2/f1
./A2/f2
./f1
./f2

The find utility on FreeBSD and NetBSD will recognize the -s flag to sort by filename:

$ find . -depth -type d -exec find -s {} -maxdepth 1 -not -type d \; 
./A1/B1/f1
./A1/B1/f2
./A1/f1
./A1/f2
./A2/B1/C1/f1
./A2/B1/C1/f2
./A2/f1
./A2/f2
./f1
./f2

As @StéphaneChazelas points out in the comments, the -s flag is not available in OpenBSD. Even where it is available, find -s also may break down depending on your locale, etc., as the man page says:

Note: ‘find -s’ and ‘find | sort’ may give different results.

3
  • 1
    find -s seems to be available in FreeBSD and NetBSD, but not OpenBSD. Commented May 15, 2023 at 19:05
  • @StéphaneChazelas Thanks for noting that. FWIW, OpenBSD's ping also doesn't implement a BSD enhancement, the -o switch. Odd.
    – Jim L.
    Commented May 15, 2023 at 19:11
  • in my Ubuntu 20.04 standard installed find command doesn't recognize -s switch|flag, as : "find: unknown predicate `-s'"
    – Tom Newton
    Commented May 16, 2023 at 11:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .