0

I am trying to join 2 files using a bash script which they look like this:

file1:

John (20)
Jim (30)
Adrian Lors (23)

file2:

Jim
some jim info here
some jim other info
more jim info

John
some john info here
some john other info
more john info

Adrian Lors
some adrian info here
some adrian other info
more adrian info

I want the expected result to look similar to this but I had no luck trying to do so:

file3:

Jim (30)
some jim info here
some jim other info
more jim info

Adrian Lors (23)
some adrian info here
some adrian other info
more adrian info

John (20) 

some john info here
some john other info
more john info

Ideally, I want the file3 to be sorted by the number in the parentheses but my main problem is that I can't join those two files. How can I achieve this within a shell script ?

Thank you

  • Do you REALLY want an extra blank line inserted after John and the order of the blocks of text from file2 rearranged? If not then please edit your question to clean it up. – Ed Morton Feb 24 at 22:17
  • 1
    With GNU awk: awk 'NR==FNR{age=$NF; NF--; a[$0]=$0; b[$0]=$0 FS age; next} {if(a[$0]==$0){print b[$0]} else {print}}' file1 file2 – Cyrus Feb 24 at 22:50
  • @Cysur you don't need 2 arrays for that approach, awk 'NR==FNR{age=$NF; NF--; b[$0]=$0 FS age; next} {if($0 in b){print b[$0]} else {print}}' file1 file2. The only problem with the approach is it'll break if any of the info lines in file2 is just a persons name from file1. That may be unlikely, idk., it depends what more john info etc. actually contains. – Ed Morton Feb 24 at 23:37
0

Using join, GNU sed and sort:

join -t '#'\
  <(sed 's/ (/#(/' file1 | sort)\
  <(sed -z 's/\n/#/g; s/##/\n/g; $ s/#$//' file2 | sort)\
  | sort -t'#' -nrk2.2\
  | sed '2,$ s/^/\n/g; s/#/ /; s/#/\n/g'

In this command the unused character # is used as a temporary field separator and newline token.

Output:

Jim (30)
some jim info here
some jim other info
more jim info

Adrian Lors (23)
some adrian info here
some adrian other info
more adrian info

John (20)
some john info here
some john other info
more john info

In detail

The first argument to join

$ sed 's/ (/#(/' file1 | sort
Adrian Lors#(23)
Jim#(30)
John#(20)

replaces the last space character on each line with the join delimiter # and sorts the result.


The second argument to join:

$ sed -z 's/\n/#/g; s/##/\n/g; $ s/#$//' file2 | sort
Adrian Lors#some adrian info here#some adrian other info#more adrian info
Jim#some jim info here#some jim other info#more jim info
John#some john info here#some john other info#more john info
  • Use sed -z to read the file to make it easier to replace newlines.
  • The first two substitutions are used to get the text for each name onto one line: Replace each newline with a # and replace two ## back to one newline.
  • The last substitution removes # at the end of the file (the last newline).
  • Sort the result.

The joined and again sorted result looks like this:

$ join -t '#'\
    <(sed 's/ (/#(/' file1 | sort)\
    <(sed -z 's/\n/#/g; s/##/\n/g; $ s/#$//' file2 | sort)\
    | sort -t'#' -nrk2.2
Jim#(30)#some jim info here#some jim other info#more jim info
Adrian Lors#(23)#some adrian info here#some adrian other info#more adrian info
John#(20)#some john info here#some john other info#more john info

Join both process substitutions on the first field and reverse numeric sort on the second field starting at position 2.


The remaining part

... | sed '2,$ s/^/\n/g; s/#/ /; s/#/\n/g'

replaces the join delimiter and brings back the newlines:

  • Add a newline at the beginning of each line starting at line 2 (the blank lines).
  • Replace the first # on each line with a space character and substitute remaining #'s with newlines.
| improve this answer | |
1

This may be what you're looking for:

$ cat tst.awk
NR==FNR {
    val = $NF
    gsub(/^[[:space:]]+|[[:space:]]+[^[:space:]]+[[:space:]]*$/,"")
    map[$0] = val;
    next
}
FNR==1 {
    FS = OFS = "\n"
    ORS = "\n\n"
    $0 = $0
}
{
    $1 = $1 " " map[$1]
    print
}

$ awk -f tst.awk file1 RS= file2
Jim (30)
some jim info here
some jim other info
more jim info

John (20)
some john info here
some john other info
more john info

Adrian Lors (23)
some adrian info here
some adrian other info
more adrian info
| improve this answer | |
0

OP:

Ideally, I want the file3 to be sorted by the number in the parentheses

Try

$ sort -t\( -k2 file1 | awk '
FNR == NR       {X = $1;
                 sub ($1 FS, "")
                 T[X] = $0
                 next
                }
                {X = $0
                 sub (/ [^ ]*$/, "", X)
                }
X in T          {print $0, T[X] 
                }
' RS= FS="\n" file2 RS="\n" ORS="\n\n" OFS="\n" -
John (20)
some john info here
some john other info
more john info

Adrian Lors (23)
some adrian info here
some adrian other info
more adrian info

Jim (30)
some jim info here
some jim other info
more jim info

sort file1 by "number in the parentheses" as desired, then read file2 and store in array indexed by $1 using empty lines as record separators, then use sort result of file1 to print array by index.

| improve this answer | |

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.