TransWikia.com

how to run awk from specific line number stored in a variable

Unix & Linux Asked on December 1, 2020

I want to get the linenumbers of the first appearance of “bar” after the apperance of “foo”.
This should run in a while loop, for the whole file. Like this:

test_file:

foo
xxx
xxx
xxx
bar
bar
xxx
bar
xxxx
xxx
xx
foo
xxx
xxx
xxx
xxx
xxx
bar

It should return:

linenumbersfoo: 1 12
linenumbersbar: 5 18

My code:

linenumbersfoo=($(awk '/foo/ {print FNR}' test_file.sh))
length="${#linenumbersfoo[@]}"

while [[ $COUNTERR -lt length  ]]; do
number=$((${linenumbersfoo["$COUNTERR"]}))

linenumbersbar[$COUNTERR]=$(awk '"$number"<=NR, /bar/ {print FNR;exit;}' test_file.sh)

let COUNTERR=COUNTERR+1 
done

echo "${linenumbersfoo[@]}"

echo "${linenumbersbar[@]}"

I get:

linenumbersfoo: 1 12
linenumbersbar: 1 1

The problem seems to be the variable “number”, if I write e.g. 5 instead of $number, it works.

Any help is very appreciated!

EDIT: The test file must look like this:

bar foo
xxx
xxx
xxx
bar
bar
xxx
bar
xxxx
xxx
xx
bar foo
xxx
xxx
xxx
xxx
xxx
bar

means, i must find the first bar after the foo, but NOT in the line with the foo

EDIT: Sorry for this, but test file didn’t show all cases, last try:

bar foo
xxx
xxx
xxx
bar
bar
xxx
bar
xxxx
xxx
xx
bar foo
xxx
xxx
bar foo
xxx
xxx
xxx
xxx
xxx
bar

expected output:

linenumbersfoo: 1 12 15
linenumbersbar: 5 15 21

If there are following to “bar foo” i want it to be found. Line 15 isn’t found.

2 Answers

EDIT: adding an improvement to match your EDIT...

one suggestion with awk:

BEGIN {
  ffoo=0; ffoos=""; fbars="";
} 
/foo/ {
  ffoo=1; ffoos=ffoos" "NR;
} 
/bar/ {
  if ((match($0, "foo") == 0) && (ffoo!=0)) {
    fbars=fbars" "NR; 
    ffoo=0;
  }
} 
END {
  print "linenumbersfoo: "ffoos"n"; 
  print "linenumbersbar: "fbars"n";
}

Answered by tonioc on December 1, 2020

Don't do this in a shell loop, awk will do it for you.

This is an awk script that you can store in a script file and invoke with awk -f script.awk filename (modified to work with recent edit of the question):

BEGIN { lookfor[0] = "foo"; lookfor[1] = "bar"; i = 0 }

$0 ~ lookfor[i] {
    lines[lookfor[i]] = lines[lookfor[i]] ? lines[lookfor[i]] ", " NR : NR
    i = (i + 1) % 2
}

END { for (n in lines) printf("%st%sn", n, lines[n]) }

Running this on your example data:

$ awk -f script.awk file
foo     1, 12
bar     5, 18

The awk script looks for the strings in the lookfor array. It first looks for the first string (foo) and when it finds it, it starts looking for the second string (bar), and then the first again in an alternating fashion. Each time it finds a string, it stores the current line number in the associative array lines for the corresponding string.

At the end, the collected line numbers in lines are outputted.

The funky-looking line

lines[lookfor[i]] = lines[lookfor[i]] ? lines[lookfor[i]] ", " NR : NR

adds the line number to the end of the string of line numbers in the lines array. If the string is empty, it just sets it to the current line number, but if it already contains something, a comma is inserted between the existing string and the line number.

You could change this to the simpler

lines[lookfor[i]] = lines[lookfor[i]] " " NR

which will insert a space between the line numbers (and in front of the first line number).

Answered by Kusalananda on December 1, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP