1

I have file like.

HR|testing file
BH|100
B2|cat|10
B2|dog|20
BT|4
BH|105
B2|apple|10
B2|banana|20
B2|melon|10
BT|5
BH|102
B2|fan|10
B2|bulb|20
B2|washer|10
B2|dryer|10
BT|6
TR|17

I need to sort the blocks based on 2nd field in BH record of each block. The rows in the blocks should remain intact. For the above file, the expected output is:

HR|testing file
**BH|100**
B2|cat|10
B2|dog|20
BT|4
**BH|102**
B2|fan|10
B2|bulb|20
B2|washer|10
B2|dryer|10
BT|6
**BH|105**
B2|apple|10
B2|banana|20
B2|melon|10
BT|5
TR|17

closed as unclear what you're asking by Hauke Laging, G-Man, Jeff Schaller, Romeo Ninov, Rui F Ribeiro Apr 25 '18 at 18:13

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • What have you tried? What didn't work? (What did work?) Where did the ** marks come from in the output? Are we supposed to add those? – roaima May 2 '18 at 21:31
3

This should work and sorts each block based on # in BH|# as block header line and in ascending BH|100, BH|102, BH|105, ... order.

awk -v RS='BH|TR' 'NR>1{ seen[NR]=$0; next } { printf $0 }
   END{ TR="TR"seen[NR]; delete seen[NR]; asort(seen);  
        for(x in seen) printf "BH"seen[x]; printf TR }' infile

HR|testing file
BH|100
B2|cat|10
B2|dog|20
BT|4
BH|102
B2|fan|10
B2|bulb|20
B2|washer|10
B2|dryer|10
BT|6
BH|105
B2|apple|10
B2|banana|20
B2|melon|10
BT|5
TR|17

  • This RS='BH|TR' defines both BH and TR as record separators (default is \newline).

  • This block NR>1{ seen[NR]=$0; next } will run for all Records but first (NR is Number of Record); so for each Record Number as the key (Index) of an associated array called seen the value of the whole record will be set to it and then read next record.

  • If it's not first Record, then { printf $0 } it. This will run only once, because next time NR>1.

At the end the block END{ ... } will be run and do:

  • This TR="TR"seen[NR] copying the last Record from the array inserted into a variable called TR and then delete it from the array delete seen[NR].
  • This asort(seen) sorts the array seen based on their values saved; then
  • We loop for(x in seen) over this array and printf "BH"seen[x]
  • At the end we print the copied TR.

If you don't mind the first and last lines in your file, you could do also:

sed '1d; $d' infile |awk '{printf $0(/^BT/?"\n":"#")}' |sort |tr '#' '\n'
-2

I found a quick and easy way to do it. Please suggest if there is a better way to do it.

Removed header and trailer records. combined and put in a # as a line separator between the lines in a block into one line. Sorted by specific field Replaced the # with new line

gawk '/^HC/ && line {print line; line=""} {line = line ? line"#"$0 : $0} END {print line}' input.txt | sort -t"|" -nk 13 | tr '#' '\n' > finaloutput.txt
  • there is no HC in your given sample data nor the commands is storing – αғsнιη Apr 25 '18 at 6:04

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