如何检查文件的第一行是否包含特定字符串?

如何检查文件的第一行是否包含特定字符串?

我需要编写一个 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

很容易检查第一行是否以#includein (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}' *

相关内容