简体   繁体   中英

In bash, how to sort strings including numbers?

I have a script that outputs file paths (via find) :

/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3-something16
/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something1
/foo/bar/example/foo/bar/example/foo-bar-example/1.2-something5
/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something2
/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something10

How can I list them in Bash so that they are in ascending numeric order based on the number at the end and regardless of the version ( 1.2, 1.2.3 or 1.2.3.4)

Ps : the something part can eventually contain a dash.

Desired output :

/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something1
/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something2
/foo/bar/example/foo/bar/example/foo-bar-example/1.2-something5
/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something10
/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3-something16

I used a temporary sentinel character to delimit the number at the end but it's a little bit complicated in my case.

Extract the number at the end, prepend it to all the lines, sort numerically and finally delete that number:

sed -r 's/(.*)([^0-9])([0-9]+)$/\3\1\2\3/' file | sort -n | sed 's/^[0-9]*//'

With your input, this returns:

/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something1
/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something2
/foo/bar/example/foo/bar/example/foo-bar-example/1.2-something5
/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something10
/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3-something16

I think this is the Schwartzian transform .


By pieces:

$ sed -r 's/(.*)([^0-9])([0-9]+)$/\3\1\2\3/' a
16/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3-something16
1/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something1
5/foo/bar/example/foo/bar/example/foo-bar-example/1.2-something5
2/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something2
10/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something10
#^
#note the numbers here

$ sed -r 's/(.*)([^0-9])([0-9]+)$/\3\1\2\3/' a | sort -n 
1/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something1
2/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something2
5/foo/bar/example/foo/bar/example/foo-bar-example/1.2-something5
10/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something10
16/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3-something16
                                                  # ^
                                                  # now it is ordered

$ sed -r 's/(.*)([^0-9])([0-9]+)$/\3\1\2\3/' a | sort -n | sed 's/^[0-9]*//'
/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something1
/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something2
/foo/bar/example/foo/bar/example/foo-bar-example/1.2-something5
/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something10
/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3-something16

You can use this sed + sort + awk :

sed -E 's/.*[^0-9]([0-9]+)$/\1 &/' file | sort -n | awk '{print $2}'

/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something1
/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something2
/foo/bar/example/foo/bar/example/foo-bar-example/1.2-something5
/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3.4-something10
/foo/bar/example/foo/bar/example/foo-bar-example/1.2.3-something16

如果您可以确保文件中永远不会有'#'字符,您可以尝试:

sed -e 's/something/#/g' filename.txt | sort -t# -k2 -n | sed -e 's/#/something/g'

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM