我需要编写一个 shell 脚本来查找并打印以字符串开头的目录中的所有文件:#include
。现在,我知道如何检查文件中是否存在字符串,方法是:
for f in `ls`; do
if grep -q 'MyString' $f; then:
#DO SOMETHING
fi
但我怎样才能将其应用到第一行呢?我想也许创建第一行的变量并检查它是否以 开头#include
,但我不确定如何执行此操作。我尝试了该read
命令,但无法读入变量。
我想听听解决这个问题的其他方法;也许 awk?不管怎样,记住,我需要检查第一行是否以 开头#include
,而不是它是否包含该字符串。这就是为什么我发现这些问题:仅当第一行匹配特定模式时如何打印文件内容?
https://stackoverflow.com/questions/5536018/how-to-print-matched-regex-pattern-using-awk
他们并没有完全提供帮助。
答案1
很容易检查第一行是否以#include
in (GNU and AT&T) sed 开头:
sed -n '1{/^#include/p};q' file
或者简化(并且 POSIX 兼容):
sed -n '/^#include/p;q' file
#include
仅当文件包含在第一行中时才会有输出。这样只需要读取第一行就可以进行检查,所以速度会非常快。
因此,所有文件(使用 sed)的 shell 循环应该如下所示:
for file in *
do
[ "$(sed -n '/^#include/p;q' "$file")" ] && printf '%s\n' "$file"
done
如果pwd 中只有文件(没有目录)。
如果您需要打印文件的所有行,则可以使用类似于发布的第一个代码的解决方案(GNU 和 AT&T 版本):
sed -n '1{/^#include/!q};p' file
或者,(BSD 兼容 POSIXfied 版本):
sed -ne '1{/^#include/!q;}' -e p file
或者:
sed -n '1{
/^#include/!q
}
p
' file
答案2
for file in *; do
[ -f "$file" ] || continue
IFS= read -r line < "$file" || [ -n "$line" ] || continue
case $line in
("#include"*) printf '%s\n' "$file"
esac
done
要打印文件的内容而不是其名称,请将printf
命令替换为cat < "$file"
.
如果您awk
支持该nextfile
扩展,并且您不关心打开非常规文件的潜在副作用:
awk '/^#include/{print substr(FILENAME, 3)}; {nextfile}' ./*
上面我们是添加./
我们稍后要删除的前缀,以FILENAME
避免包含=
字符的文件名(或名为-
)的文件名出现问题。
使用zsh
,您可以替换./*
为./*(-.)
仅将常规文件(或常规文件的符号链接,如[ -f ... ]
上述方法)传递到awk
。
或者打印文件内容而不是名称:
awk 'FNR == 1 {found = /^#include/}; found' ./*
(那个是便携式的)。
答案3
for file in *
do
[ -f "$file" ] && head -n 1 < "$file" | grep -q '^#include' && cat < "$file"
done
请注意,-q
启用该选项后,grep
即使发生错误,也会以零状态退出。
答案4
这个问题是一个完美的例子,其中 bash 和 sed 解决方案非常复杂,但使用 (GNU) awk 可以使任务变得更简单:
gawk 'FNR==1 && /^#include/{print FILENAME}{nextfile}' *