使用 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 标志设置为 true
或 false
。
$?
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
中找到查询 def
,print 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。
我正在尝试在文件中搜索关键字,从第 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 标志设置为 true
或 false
。
$?
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
中找到查询 def
,print 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。