如何折叠 'grep -c*.h *.cpp' 转化为单个计数?

如何折叠 'grep -c*.h *.cpp' 转化为单个计数?

我正在向 C++ 代码添加自测试,以确保不存在NDEBUGPosix 断言依赖项(下面的背景故事)。第一个测试寻找包含<assert.h><cassert>

FAILED=0
COUNT=$($EGREP -c '(assert.h|cassert)' *.h *.cpp)
if [[ "$COUNT" -ne "0" ]]; then
    FAILED=1
    echo "Found Posix assert headers" | tee -a "$TEST_RESULTS"
fi

其生产:

************************************
Testing: No Posix assert

./cryptest.sh: line 1130: [[: 3way: value too great for base (error token is "3way")
...

当我调试它时,我看到:

bash -x ./cryptest.sh
...

++ egrep -c '(assert.h|cassert)' 3way.h adler32.h aes.h ...
+ COUNT='3way.h:0
adler32.h:0
aes.h:0
...

因此每个文件都有自己的行和计数。

grep手册页陈述如下。它不讨论多行输出。

-c, --count
    Only a count of selected lines is written to standard output.

该行为似乎与输出控制(构成手册页)和-l, --files-with-matches.我也尝试过这个-L, --files-without-match选项。它会产生类似的错误。

我的问题是,如何将grep结果合并为一个计数?

或者也许我应该问,grep 和egrep 是适合这项工作的工具吗?如果 grep 和egrep 不是合适的工具,那么我应该使用什么?


这是一个 Bash shell 脚本,可以在我们支持的每个平台上执行。每个平台都包括 BSD、Linux、OS X、Solaris 和 Unix(以及所有移动版本,如 Android 和 iOS)。我们必须努力获得我们所需要的工具,例如grepegrep

GREP=grep
EGREP=egrep
SED=sed
AWK=awk
DISASS=objdump
DISASSARGS=("--disassemble")
...

# Fixup
if [[ "$IS_SOLARIS" -ne "0" ]]; then
    IS_X64=$(isainfo 2>/dev/null | "$GREP" -i -c "amd64")
    if [[ "$IS_X64" -ne "0" ]]; then
        IS_X86=0
    fi

    # Need something more powerful than the non-Posix versions
    if [[ (-e "/usr/gnu/bin/grep") ]]; then
        GREP=/usr/gnu/bin/grep;
    fi
    if [[ (-e "/usr/gnu/bin/egrep") ]]; then
        EGREP=/usr/gnu/bin/egrep;
    fi
    if [[ (-e "/usr/gnu/bin/sed") ]]; then
        SED=/usr/gnu/bin/sed;
    fi
    if [[ (-e "/usr/gnu/bin/awk") ]]; then
        AWK=/usr/gnu/bin/awk;
    else
        AWK=nawk;
    fi

    DISASS=dis
    DISASSARGS=()
fi

...

背景故事

我们的项目最近采取了CVE-2016-7420由于用户使用其他工具(例如 Autotools 和 CMake)构建项目。 CVE 是-DNDEBUG省略发布/生产版本的直接结果。其他工具的配置方式与我们不同,我们也没有告诉用户 (1) 他们不能使用其他构建工具,或者 (2) 用户必须定义-DNDEBUG发布/生产。

我们的补救措施NDEBUG比文档中的“简单地定义发布/生产”要深入得多。我们是NDEBUG消除对Posix 的所有依赖assert所以人们不会意外地进入配置。我们还要求用户通过定义DEBUG或 来请求调试配置_DEBUG;否则,他们将获得发布配置。

虽然后面的assertSIGART通常在发布版本中很烦人,但在调试版本中被认为是良性的,并且被认为是理所当然的,但我们观察到:

  • 我们是一个安全图书馆(我们处理敏感信息)
  • 失败的断言会将敏感信息传出到文件系统(核心文件和崩溃报告)
  • 失败的断言会将敏感信息泄露给 Apple (CrashReporter)、Apport (Ubuntu)、Microsoft (Windows 错误报告) 等平台供应商
  • 苹果、谷歌和微软等公司与政府合作挖掘敏感信息

答案1

注意:以下内容基于 的 GNU 实现grep,但我认为它也应该适用于您的情况

正如 GNUgrep手册中所述(重点是我的)

grep searches the named input FILEs for lines containing a match to the
given PATTERN.  If no files are specified, or if the file “-” is given,
grep  searches  standard  input.   By default, grep prints the matching
lines.

还,

-c, --count
       Suppress  normal output; instead print a count of matching lines
       **for each input file**.  With the -v,  --invert-match  option  (see
       below), count non-matching lines.

(默认行为是在此类输出中添加文件名前缀 - 尽管可以使用该-h选项来抑制)。

通过将目标文件连接到单个输入流并将其传输到grep,您应该能够覆盖这两种行为并获得不带前缀的单个计数:

COUNT=$(cat *.h *.cpp | $EGREP -c '(assert.h|cassert)')

恕我直言,这将有资格作为有用使用猫;可能你被建议反对的是猫的无用使用

答案2

钢铁司机的回答当我读到你的问题标题时, (do )是我的第一个想法。但我发现,在您的脚本片段中,除了将其与零进行比较之外,您没有使用计数 - 即,您问“有多少个?”当你想知道“有吗?”时。考虑使用:cat files | grep -c <token>-q

if "$EGREP" -q -- 'assert\.h|cassert' *.h *.cpp
then
    FAILED=1
    echo "Found Posix assert headers" …
fi

笔记:

  • 您应该始终引用您的 shell 变量引用(例如,"$EGREP"),除非您有充分的理由不这样做,并且您确定您知道自己在做什么。如果您已经定义了EGREP=grep -e,那么这将是一个不带引号的合理理由$EGREP,但请参阅这个答案忘记在 bash/POSIX shell 中引用变量的安全隐患
  • -q(或,等效地,--quiet--silent)表示“安静;不要向标准输出写入任何内容。如果发现任何匹配,即使检测到错误,也会立即以零状态退出。”这不仅为您提供了所需的功能行为(即与 Steeldriver 的答案相同的功能行为),而且具有grep一旦找到匹配项就会退出的性能优势,并且不需要读取所有文件。 
  • 建议将其放在--命令的选项及其参数之间,以防止以 开头的文件名- 被解释为选项字符串。
  • 整个正则表达式不需要用括号括起来。
  • grep 'assert.h'将匹配assert hassert,hassert3hassertph等。如果您不在乎,那由您决定。如果您只想匹配assert.h,请 grep for assert\.h

相关内容