2

If I have the following file:

0|0 1|1 1|0
1|1 0|0 0|0
0|1 1|0 0|1
1|0 0|1 1|1

How can I print the field corresponding to where NR==3 is 1 so I would get the following output:

0 1 0
1 0 0
1 1 1
0 0 1

I've tried:

awk '{ split ($1, x, "|"); if ((NR==3) && (x[2]==1)) print x[2]; else if (NR==2) print x[1] }' input

This code evaluates only one field as I am not certain how to integrate for (i=1;i<=NF;i++) into it and it only prints the value of the third record, not all records.

Please let me know if any clarification is necessary.

Thank you!

3
  • 2
    You need to explain your requirement more clearly. I think you might be confused about what NR means - it refers to the record (line) number. So NR==3 on the third line. Commented Nov 15, 2015 at 22:41
  • Yes, so essentially if we focus on the third record, find if the 1 is left or right of the | and print all records on the same side as the 1 on the third record. Does this make more sense? Commented Nov 15, 2015 at 22:43
  • 3
    @Rish: no, frankly your explanation makes no sense yet. The third record in the input is the line 0|1 1|0 0|1 — are we agreed on that? And given that you have not specified a field separator, there are 3 fields on the line, with values 0|1 and 1|0 and 0|1 — are we agreed on that? You then say (in a comment) 'print all records on the same side as the 1 on the third record'; since there are 3 ones in the third record, it isn't clear what you mean at all. Commented Nov 15, 2015 at 23:44

1 Answer 1

1

Since you need the third line (as a key line) to process each other, you must first store all the lines in an array and once the end of the file is reached build the result (In the END part of the awk script).

You can do it using the bitwise operator and. First you need to use [ |] as field separator (the space or the pipe). Each line contains six 0 or 1 and can be seen as a binary number.

awk -F'[ |]' '{ a[NR] = $1*32 + $2*16 + $3*8 + $4*4 + $5*2 + $6 } # base10 conversion
  END {
      for(i=1;i<=NR;i++) {
          l=and(a[i], a[3]);
          print (and(l,48)?1:0)" "(and(l,12)?1:0)" "(and(l,3)?1:0);
      }
  }' file.txt

The script converts each line to an integer and store it in an array. At the end, using the and bitwise operator, each line is processed with the third line:

             Binary               Integer
line1:       0 0 1 1 1 0          13 
line3(key):  0 1 1 0 0 1          25
            -------------        ----
and:         0 0 1 0 0 0           8        (l)

After this operation, only bits set to 1 in the current line and in the third line (key line) at the same time remains.

Then to know if you need to display a 1 or a 0 for each group of two fields, you only need to test with the same operation but this time with 110000, 001100, 000011 (i.e. 48, 12, 3) if the result is null or not:

                      Binary              |       Integer
           -------------------------------+-------------------
l:          001000    001000    001000    |    8     8     8
            110000    001100    000011    |   48    12     3
           --------  --------  --------   |  ----  ----  ----
and:        000000    001000    000000    |    0     8     0
not null:        0         1         0    |    0     1     0

Note: you can easily change the script to make it work with any number of fields. Use a for loop for the base10 conversion of lines and calculate the "masks" (3,12,48,...) needed (you could, for example, shift 3 (11) two bits on the left for each field).

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.