ZmirBurger
ZmirBurger

Reputation: 113

find text in file starting from nth line onwards, using shell script

I am trying to search for a keyword in a file, starting from the nth line onwards. And if its found, set a variable to true For example, file contents of testtext.txt:

abc-0
def-0
xyz-0
abc-1
xxx-1

I want to search keyword 'def' from the 3rd line onwards and if found, print true. Also assuming the nth line is a pass in value.

I have a rough idea below, but not sure how to fix it.

#!/bin/bash

keyword=def
nthline=3
flag=false

count=$(awk 'NR>$nthline' ./testtext.txt | grep '$keyword' | wc -l)
if [[ $count -gt 0 ]];then
  flag=true
fi

echo $flag

Expected output should be false, because def doesnt exist on the 3rd line onwards.

Problem is I cannot pass in the 'nthline' variable into the NR> of awk and also cannot pass in $keyword to the grep on that line

I have tested using actual values instead of pass in, it works, so the commands should be working.

count=$(awk 'NR>3' ./testtext.txt | grep 'def' | wc -l)

Is there any way to do this, or any other better solution?

EDIT: Got a solution that is almost 90% there, but I need to assign the variable after the flag is true. this is what I have so far, I am not sure if there is any better way of doing it.

#!/bin/bash

flagz=false
tgt="def"
n=$1

flagz=$(awk -v tgt="$tgt" -v n="$n" flag="$flagz"'
FNR>=n && index($0,tgt){
    print("true1")
    exit
}
' ./testtext.txt)

echo "$flagz"

Upvotes: 3

Views: 739

Answers (4)

Dudi Boy
Dudi Boy

Reputation: 4865

Suggesting:

awk 'NR > 3 && /def/{print 1}' testtext.txt

NR > 3 means if the total number of records greater than 3 && the query def in /def was found, print 1.

Upvotes: 0

dawg
dawg

Reputation: 103774

It would be best to do this entirely in awk since you are already using awk to slice the file.

Example:

tgt="def"
n=3

awk -v tgt="$tgt" -v n="$n" '
BEGIN{flag="false"}
FNR>=n && index($0,tgt){
    flag="true"
    exit
}
END{print flag}' file

Alternatively, you can make a pipe and then inspect $? to see if grep found your match:

tgt="def"
n=2

tail -n "+$n" file | grep "$tgt" >/dev/null

Now $? will be 0 if the grep finds the pattern and 1 if it is not found. Then you can set a flag like so:

flag="false"
tail -n "+$n" file | grep "$tgt" >/dev/null 
[ $? ] && flag="true"

Now flag is set true / false based on the grep. The command tail -n +[some number] file will print the file contents from the absolute line number onward.

For large files, the awk is significantly more efficient since it exits on the first match.


Edit based on update.

The issue is setting a Bash flag to true or false based on a process.

The $? Special Parameter is set based on the exit status of the most recently executed foreground pipeline. So pick your method to slice the file and detect the string and then set the flag in your script based on $? immediately after the pipe. Be aware that testing $? resets $? before it is tested -- so you either need to capture the value of $? before the test or use it in the pipeline itself.

These methods work:

1) Capture $? and test:

awk -v tgt="$tgt" -v n="$n" -v flag=1 '
FNR>=n && index($0,tgt){
    flag=0
    exit
}
END{ exit flag }
' ./testtext.txt 
res=$?
[ $res -eq 1 ] && flagz=false || flagz=true

2) Capture a string result and test that:

res=$(awk -v tgt="$tgt" -v n="$n" -v flag="false" '
FNR>=n && index($0,tgt) {
    flag="true"
    exit
}
END{ print flag }' ./testtext.txt)

[ $res = "false" ] && flagz=false || flagz=true

3) Use a pipe and test in the pipe:

tail -n "+$n" file | grep "$tgt" >/dev/null && flagz=true || flagz=false 

My preference is 3 for small files and 2 for big files.

Upvotes: 3

David C. Rankin
David C. Rankin

Reputation: 84551

Another way to do this is with sed and a range expression telling sed to search from line 3 to end 3,$. The types of expressions that accept addr1,addr2 is limited but does include the traditional substitute form of sed 's/find/replace/. Where you can search for the entire line containing def and then replace it with & (shorthand for the entire matched expression).

To control printing pass the -n option (suppress normal printing of pattern space) and add p (print) at the end of the substitute expression, e.g.

sed -n '3,$ s/^.*def.*$/&/p' file

The above skips the first two lines and begins the search/replacement on line 3.

Upvotes: 1

pjh
pjh

Reputation: 8064

Try:

tail -n "+$nthline" testtext.txt | grep -qF -- "$keyword" && flag=true || flag=false

Upvotes: 0

Related Questions