1

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.

0

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";
}
  • Thank you, this works. But now found out my test file isn't really representing my real problem. See EDIT in question. I tried with getline, didn't work – Julian Mar 8 '18 at 11:59
0

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("%s\t%s\n", 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).

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.