Reputation: 1806
I have a list of files, with full path, that I need to sort in a bash shell.
The list will look like
/total/path/software/version1.2.3.4/filename.10.cfg -- infomation grepped
/full/path/software/version1.2.3.4/filename.1.cfg -- infomation grepped
/long/path/software/version1.2.3.4/filename.2.cfg -- infomation grepped
/full/path/software/version1.2.3.4/filename.12.cfg -- infomation grepped
/long/path/software/version1.2.3.4/filename.1.cfg -- infomation grepped
/full/path/software/version1.2.3.4/filename.3.cfg -- infomation grepped
/long/path/software/version1.2.3.4/filename.18.cfg -- infomation grepped
/full/path/software/version1.2.3.4/filename.20.cfg -- infomation grepped
/real/path/software/version1.2.3.4/filename.4.cfg -- infomation grepped
/total/path/software/version1.2.3.4/filename.5.cfg -- infomation grepped
I need to have the list first sorted by the path, and then by the filename number.
I've tried:
sort -t'.' -k 1,1 -k 2,5n fileame.txt
But it only ever sorts by the path. If I do:
sort -t'.' -k5n filename.txt
It works fine. How can I get the filenames in numeric order, after sorting by path?
Thanks
Upvotes: 2
Views: 137
Reputation: 107040
I would create a sort key, and then sort on that sort key, then remove the sort key:
Let's see...
$ while read line
do
dirname=${line%/*} #Directory names
number=$(echo "$line" | sed 's/.*\.\([0-9]*\)\.cfg.*/\1/') # File number
printf "%-60.60s %04d | %s\n" "$dirname" "$number" "$line"
done < filetext.txt | sort | sed "s/.* \| //"
This is reading in each line from filetext.txt
and piping it into the while read line
loop.
The dirname
is using the ${parameter%word}
feature in BASH. This takes the value of ${parameter}
and removes the smallest amount from the right side that matches the pattern word
. Thus, ${line%/*}
is taking $line
, and is removing the last forward slash and all characters after that.
The number
was a bit trickier. I noticed that you had something like .44.cfg
at the end of the file name. That meant if I could find that particular pattern, I could find the file number. My sed
command looks for a period, followed by zero or more numbers, followed by .cfg
., and marks the numbers as a grouping. I then replace the entire line with the first grouping giving me the number.
Next, I print out the directory and the number using printf
. I space fill the directory name to sixty characters (that could be increased if needed) and then a four digit number. This creates a sort key that looks like this:
/full/path/software/version1.2.3.4 0001
/full/path/software/version1.2.3.4 0003
/full/path/software/version1.2.3.4 0012
/full/path/software/version1.2.3.4 0020
/long/path/software/version1.2.3.4 0001
/long/path/software/version1.2.3.4 0002
/long/path/software/version1.2.3.4 0018
/real/path/software/version1.2.3.4 0004
/total/path/software/version1.2.3.4 0005
/total/path/software/version1.2.3.4 0010
I append the line to this sort key, and then do my sort. After that, I remove the sort key from the line. The results:
/full/path/software/version1.2.3.4/filename.1.cfg -- infomation grepped
/full/path/software/version1.2.3.4/filename.3.cfg -- infomation grepped
/full/path/software/version1.2.3.4/filename.12.cfg -- infomation grepped
/full/path/software/version1.2.3.4/filename.20.cfg -- infomation grepped
/long/path/software/version1.2.3.4/filename.1.cfg -- infomation grepped
/long/path/software/version1.2.3.4/filename.2.cfg -- infomation grepped
/long/path/software/version1.2.3.4/filename.18.cfg -- infomation grepped
/real/path/software/version1.2.3.4/filename.4.cfg -- infomation grepped
/total/path/software/version1.2.3.4/filename.5.cfg -- infomation grepped
/total/path/software/version1.2.3.4/filename.10.cfg -- infomation grepped
Note I'm not depending upon a particular format for the file name as others have in their answers. What if you had a line like this?
/total/path/software/version1.2/filename.10.cfg -- infomation grepped
There aren't five decimal places in that line. Anything that is attempting to sort by breaking the fields via the periods will fail. The above will still work.
Upvotes: 1
Reputation: 23364
You need to sort up to filename
first and then specify the filename
number as a tie-breaker
sort -t'.' -k1,4 -k5n,5n filename.txt
/full/path/software/version1.2.3.4/filename.1.cfg -- infomation grepped
/full/path/software/version1.2.3.4/filename.3.cfg -- infomation grepped
/full/path/software/version1.2.3.4/filename.12.cfg -- infomation grepped
/full/path/software/version1.2.3.4/filename.20.cfg -- infomation grepped
/long/path/software/version1.2.3.4/filename.1.cfg -- infomation grepped
/long/path/software/version1.2.3.4/filename.2.cfg -- infomation grepped
/long/path/software/version1.2.3.4/filename.18.cfg -- infomation grepped
/real/path/software/version1.2.3.4/filename.4.cfg -- infomation grepped
/total/path/software/version1.2.3.4/filename.5.cfg -- infomation grepped
/total/path/software/version1.2.3.4/filename.10.cfg -- infomation grepped
Upvotes: 1
Reputation: 17552
is this what you are looking for ?
Kaizen ~
$ for ch in `sort testfile.txt | cut -c2-3 | uniq `
> do
> sed -n "/^\/$ch/p" testfile.txt | sort -t'.' -k5n ;
> done ;
result :
/full/path/software/version1.2.3.4/filename.1.cfg -- infomation grepped
/full/path/software/version1.2.3.4/filename.3.cfg -- infomation grepped
/full/path/software/version1.2.3.4/filename.12.cfg -- infomation grepped
/full/path/software/version1.2.3.4/filename.20.cfg -- infomation grepped
/long/path/software/version1.2.3.4/filename.1.cfg -- infomation grepped
/long/path/software/version1.2.3.4/filename.2.cfg -- infomation grepped
/long/path/software/version1.2.3.4/filename.18.cfg -- infomation grepped
/real/path/software/version1.2.3.4/filename.4.cfg -- infomation grepped
/total/path/software/version1.2.3.4/filename.5.cfg -- infomation grepped
/total/path/software/version1.2.3.4/filename.10.cfg -- infomation grepped
the approach is same as yours , i just added sed !!
Upvotes: 1