xev | awk -F'[ )]+' '/^KeyPress/ { a[NR+2] } NR in a { printf "%-3s %s\n", %5, %8}

When I use xev there is only a certain bit of information I want. The natural response of using xev to get keycode info looks like this...

KeyPress event, serial 48, synthetic NO, window 0x1600001,
    root 0xf6, subw 0x0, time 754405, (348,566), root:(349,620),
    state 0x0, keycode 40 (keysym 0x64, d), same_screen YES,
    XLookupString gives 1 bytes: (64) "d"
    XmbLookupString gives 1 bytes: (64) "d"
    XFilterEvent returns: False

KeyRelease event, serial 48, synthetic NO, window 0x1600001,
    root 0xf6, subw 0x0, time 754488, (348,566), root:(349,620),
    state 0x0, keycode 40 (keysym 0x64, d), same_screen YES,
    XLookupString gives 1 bytes: (64) "d"
    XFilterEvent returns: False

The result of the AWK script would only return:

40 d

This made me want to learn AWK :)

So after learning about NR and doing a few tutorials, I am now trying to figure this out. First the -F is just divides by fields in this case '[ )]+' I think this is regex for 1 or more of spaces or closing parenthesis. I do not understand this. I do not see any spaces before prenthesis. Also, I do not know what a space in a regex box does here, because I have only learned about whitespace tools such as \s. So I wanted to see what fields dispay with $5 and %8 because it didnt look right in my analysis and I was confused!!

echo "state 0x0, keycode 12 (keysym 0x33, 3), same_screen YES," | awk '{print $8}'
echo "state 0x0, keycode 12 (keysym 0x33, 3), same_screen YES," | awk '{print $5}'

edit: So what is this printf "%-3s %s\n", $5, $8}?? Why is the output so different then my echo example above?

Obviously, this is coming from the magic of {a[NR+2] NR in a}. Some sort of an array and a for loop.

I look at NR+2 and it makes me think: since when AWK starts NR starts on 1 and adding 2 would make it the third line. This looks right since all of the info I want is on the third line.

What is going on with a[NR+2]? for NR in a printf... ? I understand printf I understand for loops. The way NR is used here baffles me.

I guess the real question is what is happening with 'a'? Is this a predefined thing I don't know about?

  • You mean $5 and $8 rather than %5 and %8, surely? You appear to have figured out what the { a[NR+2] } NR in a ... does - so what is your question, exactly? Commented Aug 25, 2016 at 20:08

3 Answers 3


You seem to have correctly deduced what {a[NR+2]} NR in a { ... }} does;

  • /^KeyPress/ {a[NR+2]} creates an (empty valued) element in array a with index NR+2, when the start of line NR matches the string KeyPress
  • NR in a is therefore true for the line two lines below where /^KeyPress/ matched

In that respect, it could perhaps have been written more transparently as

awk -F'[ )]+' '/^KeyPress/ {n=NR+2} NR==n { printf "%-3s %s\n", $5, $8}'

A possibly more tricky question is why the fields to be printed are $5 and $8 rather than $4 and $7; that's because the treatment of initial whitespace is different when using a non-default field separator: from the Default Field Splitting section of the GNU awk manual:

Fields are normally separated by whitespace sequences (spaces, TABs, and newlines), not by single spaces. Two spaces in a row do not delimit an empty field. The default value of the field separator FS is a string containing a single space, " ". If awk interpreted this value in the usual way, each space character would separate fields, so two spaces in a row would make an empty field between them. The reason this does not happen is that a single space as the value of FS is a special case—it is taken to specify the default manner of delimiting fields.

If FS is any other single character, such as ",", then each occurrence of that character separates two fields. Two consecutive occurrences delimit an empty field. If the character occurs at the beginning or the end of the line, that too delimits an empty field. The space character is the only single character that does not follow these rules.

  • This answer is great! I appreaciate the n=NR+2 example. Why does the /n in the print function cause the comma to dissapear? Why would it not have to be [ ),]+ ??? Commented Aug 26, 2016 at 0:09
  • Thanks! the \n (notice it's a backslash, not a forward slash) tells printf to insert a newline at the end of each output. Not sure what comma you're referring to? If it's the one after the d) i.e. in d), then that becomes the ninth field - try changing it to printf "%-3s %s %s\n", $5, $8, $9 and you will see. Commented Aug 26, 2016 at 0:18

%-3s means to print a string in a field that's 3 characters wide, with padding spaces on the right rather on the left. So it will print

40  d

rather than

 40 d

I can help a bit, but someone here should be able to provide a more in-depth answer. But, let's break it down.

First, you're piping the output from xev into awk with the |

You're correct that -F is defining the column separator character for Awk, and the regular expression to match the character is [ )]+

it will match only once in the sample output you provided. Everything after that is the awk script.

/^KeyPress/ is another regular expression, looking for a newline beginning with "KeyPress"... which seems redundant.

The arguments for printf are comma separated, with the first one being the formatting, so: "%-3s %s\n" is the formatting. See and here

Is it possible that {printf "%-3s %s\n", %5, %8} is meant to be { printf "%-3s %s\n", $5, $8} ?

I can't quite make sense of this. Hopefully someone else can decode it! BTW - you can use this to help test regex.

  • You are correct. That was a typo. I was mainly baffled by how a[NR+2] for NR in a worked. Thabks for the response. Commented Aug 25, 2016 at 23:27

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .