3

I'm trying to sort the rows of a text file based on an 'index' in another file, such that the text file is arranged, row by row, in the same order as the index file.

The following code achieves what I'm looking to do, but I want to replace file_to_sort.txt with another variable (so that I can pass 2 command line arguments) to make this a general script. I can't figure out how to make the while loop deal with this.

while read line ; do grep $line file_to_sort.txt ; done < ../index.txt

The file to sort looks like so:

Locus ./PAK_01896.fsa GC: 0.401826484018
Locus ./PAK_02014.fsa GC: 0.355555555556
Locus ./PAK_02606.fsa GC: 0.415555555556
Locus ./PAK_03203.fsa GC: 0.391111111111
Locus ./PAU_01961.fsa GC: 0.395555555556
Locus ./PAU_02074.fsa GC: 0.406392694064
Locus ./PAU_02206.fsa GC: 0.353333333333
Locus ./PAU_02775.fsa GC: 0.415555555556
Locus ./PAU_03392.fsa GC: 0.384444444444
Locus ./PLT_01696.fsa GC: 0.42
Locus ./PLT_01716.fsa GC: 0.422222222222
Locus ./PLT_01736.fsa GC: 0.433333333333
Locus ./PLT_01758.fsa GC: 0.426666666667
Locus ./PLT_02424.fsa GC: 0.413333333333
Locus ./PLT_02568.fsa GC: 0.391111111111

And the index order looks like this (and so the output should be the first file, sorted according the the ./xxx_xxxx strings in the second).

PAU_03392
PAK_03203
PAU_01961
PAK_01787
PLT_02568
PAU_02074
PAK_01896
PLT_02424
PAU_02775
PLT_01696
PAK_02606
PLT_01736
PLT_01758
PLT_01716
PAU_02206
PAK_02014

I'm sure this is really simple, but I can't see it and whatever I was searching to try and solve this wasn't dragging up the right answers (since I'm sure this has been answered somewhere.

  • how bigs the file. can it fit in memory ? Also in the sort file can there be dupes of the keys ? – 123 Apr 6 '16 at 8:31
  • @nagu please don't answer questions in the comments. That way, the question will never be marked as answered. Post your comment as an answer instead. – terdon Apr 6 '16 at 8:53
  • 1
    Your question is misleading. If you know how to do it why are you using a title like "Sorting one file line by line based on another file" and why are you posting the contents of the files ? That's irrelevant. Also, your while..read..grep is anything but fast. You should use something like awk -F'[/.]' 'NR==FNR{x[$3] = $0;next};($1 in x) {print x[$1]}' "$1" "$2" I'll let you figure out what the $1 and respectively $2 stand for (this is what your question is all about, after all). – don_crissti Apr 6 '16 at 10:55
  • @123 The file is pretty small (for this particular usage case, though my wanting to generalise it means that I can envisage usage cases in the future where the files could be pretty large). And, again, at least in this particular instance there are no duplicated keys. – Joe Healey Apr 7 '16 at 9:00
  • 1
    @don_crissti I thought the title was descriptive of the problem I was trying to solve, and was the types of phrases I was personally searching for when looking for solutions. I gave the file contents simply because I thought it might make the problem that bit clearer. I can edit the question though if people disagree. – Joe Healey Apr 7 '16 at 9:03
1
#!/bin/bash
FILE_TO_SORT="$1"
INDEX_FILE="$2"
TMP_FILE=$(mktemp)

while read LINE; do
    grep "$LINE" "$FILE_TO_SORT" >>"$TMP_FILE"
done <"$INDEX_FILE"

mv -f "$TMP_FILE" "$FILE_TO_SORT"

The above script will take 2 arguments and sort the first based on the second, take in consideration that lines not matched by any in the index file will be discarded, also the original file will be replaced by the sorted one. On top of that if you have several lines to be sorted with the same index and want it to be sorted as well you can change the grp line to this grep "$LINE" "$FILE_TO_SORT" | sort -f 4 >>"$TMP_FILE" and it will be sorted by the number (field 4) at the end.

  • To cut out the 'middle man' - is it valid to simply use: while read LINE ; do grep "$LINE" "$1" >> "$3" ; done < "$2" - and then to invoke it with script.sh $1 $2 $3 ? – Joe Healey Apr 7 '16 at 8:57
  • Of course you can use the ${1,2} variables directly but I always use named variables because is easier to read specially on long scripts, and also you can pass a third argument to specify an output file, or even remove the redirect after grep and output to stdout, but those are details I left for the OP to decide – Dalvenjia Apr 7 '16 at 15:13
0

if all you want to do is replace the hard coded file name to be sorted and the input file by variables, this will work: input_file="$1"; file_to_sort="$2"; while read line ; do grep $line $file_to_sort ; done < $input_file then, run it by passing ../index.txt and file_to_sort.txt as arguments to the script.

also, note that in your script, if there are lines in the file_to_sort that do not match any lines in index.txt, those will be omitted in the output.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.