linuxshellsortingunixpath

How to sort Linux paths with --field-separator='/'?


Input:

d1/d2a/d3
d1/d2/d3
d1/d2+/d3

Desired result:

d1/d2/d3
d1/d2+/d3
d1/d2a/d3

Please note: The directory name may include any characters, except forward slash /.

Try 01: Normal sort does not work. It sorts punctuation marks + (ASCII: 43) before forward slash / (ASCII: 47).

$ { echo 'd1/d2a/d3' ; echo 'd1/d2/d3' ; echo 'd1/d2+/d3' ;} \
| LC_ALL=C sort --debug

sort: text ordering performed using simple byte comparison
d1/d2+/d3
_________
d1/d2/d3
________
d1/d2a/d3
_________

Try 02: I am not sure. It seems that using --field-separator without --key has no effect.

$ { echo 'd1/d2a/d3' ; echo 'd1/d2/d3' ; echo 'd1/d2+/d3' ;} \
| LC_ALL=C sort --debug --field-separator='/'

sort: text ordering performed using simple byte comparison
d1/d2+/d3
_________
d1/d2/d3
________
d1/d2a/d3
_________

Try 03: I am not sure. It seems that using --key=1 does not sort every fields separately.

$ { echo 'd1/d2a/d3' ; echo 'd1/d2/d3' ; echo 'd1/d2+/d3' ;} \
| LC_ALL=C sort --debug --field-separator='/' --key=1

sort: text ordering performed using simple byte comparison
d1/d2+/d3
_________
_________
d1/d2/d3
________
________
d1/d2a/d3
_________
_________

Try 04: The desired result can be archived by explicitly listing ALL keys. But the depth of path can be unlimited.

$ { echo 'd1/d2a/d3' ; echo 'd1/d2/d3' ; echo 'd1/d2+/d3' ;} \
| LC_ALL=C sort --debug --field-separator='/' --key=1,1 --key=2,2 --key=3,3

sort: text ordering performed using simple byte comparison
d1/d2/d3
__
   __
      __
________
d1/d2+/d3
__
   ___
       __
_________
d1/d2a/d3
__
   ___
       __
_________

Note: I also tried adding --stable or using --version-sort. The results are the same.


Solution

  • Thanks everyone and I revisited the specification of filesystems. Other than forward slashes /, the null character is also not allowed in path. And thanks God that the null character is having ASCII: 0.

    The solution is indeed as simple as:

    $ { echo 'd1/d2a/d3' ; echo 'd1/d2/d3' ; echo 'd1/d2+/d3' ;} \
    | tr -- '/' '\0' | LC_ALL=C sort --debug | tr -- '\0' '/'
    
    sort: text ordering performed using simple byte comparison
    d1/d2/d3
    ______
    d1/d2+/d3
    _______
    d1/d2a/d3
    _______
    

    Thank you to all, again, for solving this with me.