Gammerx
Gammerx

Reputation: 9

Grep and ignoring leading whitespace

I've been working on a script to search for names of defines, and then grab the hex values for them and put them in the list. Once I have the list of names I'll attempt to search for "#define [name]" using -w to ensure exact match, and then awk '{ print $3 }' to grab the hex value.

However it works if the line is similar to

a.h:#define [name] 0x000

But it does NOT work if it is similar to

a.h:    #define [name] 0x000

How can I get around this? I have tried this

grep -nrw "\s*#define[[:space:]]*$p" . --include=*.h | awk '{ print $3 }'

I thought the \s* would ignore the leading whitespace before #define but it doesn't. Am I doing something wrong?

Upvotes: 0

Views: 4805

Answers (4)

karakfa
karakfa

Reputation: 67567

If it's the last element, you can always use

... | awk '{print $NF}'

You can also filter in awk as well

awk '/#define/{print $NF}'

This will print the second field after a match with "#define"

awk '{for(i=1;i<NF-1;i++) if($i~"#define") print $(i+2)}'

Upvotes: 1

John1024
John1024

Reputation: 113984

Let's take this as our sample input:

$ cat a.h
other name 0x100
#define name 0x000
    #define name 0x001

Using awk

$ p=name; awk -v v="$p" '$1=="#define" && $2==v{print $3}' a.h
0x000
0x001

If we want to require that the printed value is a hex number:

$ p=name; awk -v v="$p" '$1=="#define" && $2==v && $3 ~ /^0x[[:xdigit:]]+$/{print $3}' a.h
0x000
0x001

Using sed

$ p=name; sed -nE "s/\s*#define\s+$p\s+([[:xdigit:]]*)/\1/p" a.h
0x000
0x001

If we want to be a bit more strict about what we match:

$ p=name; sed -nE "s/\s*#define\s+$p\s+(0x[[:xdigit:]]*)$/\1/p" a.h
0x000
0x001

awk and leading spaces

The presence of leading spaces does not change how awk numbers a field:

$ echo '   1 2' | awk '{for (i=1;i<=NF;i++)printf "field %s = %s\n",i,$i;}'
field 1 = 1
field 2 = 2
$ echo '1 2' | awk '{for (i=1;i<=NF;i++)printf "field %s = %s\n",i,$i;}'
field 1 = 1
field 2 = 2

Upvotes: 0

zolo
zolo

Reputation: 469

On linux this may work perfectly:

grep -Poinr "#define\s+\[.*?\K[0-9a-f]+x[0-9a-f]+" . --include=*.h

Upvotes: 1

Peter Cordes
Peter Cordes

Reputation: 365781

grep still matches the lines you want, but the whitespace between the line number and the #define makes it a separate awk field.

find -iname '*.[ch]' -exec sed -nse 's/.*\(#define\s*[a-zA-Z0-9_]*\)\(.*\)/\1: \2/p' {} +

Replace [a-zA-Z0-9_]* with your $p, if you want to run this search over the full text for every symbol separately. (It'd be faster to extract a list of all the defines once, and then search it.)

I used sed -s in case you want to add a sed command to print the line number. You were using grep -n and then throwing away the line number with awk, so I figured maybe you were showing a cut-down example of how you really use the grep output.

You could also use GNU grep -o to print only the part of the line that matches your pattern. Then instead of having your pattern match whitespace the grep output lines will always look like

number:#define ...

like your awk command is expecting.

Upvotes: 0

Related Questions