使用 shell 脚本从第 n 行开始查找文件中的文本

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

我正在尝试在文件中搜索关键字,从第 n 行开始。 如果找到,将变量设置为 true 例如 testtext.txt 的文件内容:

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

我想从第 3 行开始搜索关键字 'def',如果找到,打印 true。 还假设第 n 行是传递值。

我在下面有一个大概的想法,但不确定如何解决它。

#!/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

预期的输出应该是假的,因为 def 在第 3 行之后不存在。

问题是我无法将 'nthline' 变量传入 awk 的 NR> 并且也不能将 $keyword 传递给该行的 grep

我已经使用实际值而不是传入值进行了测试,它有效,所以命令应该有效。

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

有什么方法可以做到这一点,或者有其他更好的解决方案吗?

编辑: 得到了几乎 90% 的解决方案,但我需要在标志为真后分配变量。这是我目前所拥有的,我不确定是否有更好的方法。

#!/bin/bash

flagz=false
tgt="def"
n=

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

echo "$flagz"

最好完全在 awk 中执行此操作,因为您已经在使用 awk 对文件进行切片。

示例:

tgt="def"
n=3

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

或者,您可以制作一个管道,然后检查 $? 以查看 grep 是否找到您的匹配项:

tgt="def"
n=2

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

现在,如果 grep 找到模式,$? 将是 0,如果未找到,则将是 1。然后你可以像这样设置一个标志:

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

现在 flag 基于 grep 设置为真/假。命令tail -n +[some number] file将从绝对行号开始打印文件内容。

对于大文件,awk 的效率要高得多,因为它会在第一次匹配时退出。


根据更新进行编辑。

问题是根据进程将 Bash 标志设置为 truefalse

$? Special Parameter 是根据最近执行的前台管道的 退出状态设置的。 所以选择你的方法来切片文件和检测字符串,然后在管道后立即根据 $? 在脚本中设置标志。请注意 testing $? 在测试之前重置 $? - 因此您需要在测试之前捕获 $? 的值或使用它在管道本身中。

这些方法有效:

1) 捕获 $?并测试:

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

2) 捕获字符串结果并测试:

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

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

3) 使用管道并在管道中进行测试:

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

我的偏好是 3 用于小文件,2 用于大文件。

建议:

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

NR > 3 表示如果记录总数大于 3 && 则在 /def 中找到查询 defprint 1.

尝试:

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

另一种方法是使用 sed 和一个范围表达式告诉 sed 从第 3 行搜索到结束 3,$。接受 addr1,addr2 的表达式类型有限,但确实包括 sed 's/find/replace/ 的传统替代形式。您可以在其中搜索包含 def 的整行,然后将其替换为 &(shorthand 用于整个匹配表达式)。

要控制打印通过-n选项(抑制模式space的正常打印)并在替代表达式的末尾添加p(打印),例如

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

上面跳过前两行并在第 3 行开始 search/replacement。