当空行可以通过管道传输到 wc 时更好的 shell 解决方案

当空行可以通过管道传输到 wc 时更好的 shell 解决方案

我有执行类似操作的代码:

#!/bin/sh
CONTENTS=$(cat "somefile")
RELEVANT_LINES=$(echo "$CONTENTS" | grep -E "SEARCHEXPR")
COUNT=$(echo "$RELEVANT_LINES" | wc -l)

令我烦恼的是,与通过将第三行替换为以下内容给出的正确输出相比,如果没有任何匹配项,此代码不会输出相同的输出:

COUNT=$(echo "$CONTENTS" | grep -E "SEARCHEXPR" | wc -l)

我最终追踪到这样一个事实:当没有任何匹配项时,RELEVANT_LINES 被设置为空字符串,而 echo 输出一行空字符串 + \n ,行数为 1。

我尝试在第三行中使用 printf 和 echo -n ,但找不到优雅的解决方法,最终使用COUNT=$(echo "$RELEVANT_LINES" | grep '0' | wc -l)(所有行都包含零)来避免必须用正则表达式过滤整个源文件两次。

这不可能是正确的,但我无法找出正确的解决方案。

我没有放弃编写脚本,-eq ''因为我不确定它是否会那么强大,而且我更喜欢直接使用管道来wc获得纯粹的整洁。

有任何提示如何获取变量中的文件内容,以便在通过 grep 过滤后整齐地区分零行和一行吗? :)

答案1

使用CONTENTS再次保存文件echo有点多余,你可以

lines=$(cat "somefile" | grep -E "SEARCHEXPR")

更确切地说

lines=$(grep -E "SEARCHEXPR" "somefile")

如果您只想要匹配行的数量,请使用grep -c

count=$(grep -c -E "SEARCHEXPR" "somefile")

您看到的直接问题是由echo始终输出换行符这一事实引起的。如果您从命令替换中获得至少一行,这实际上会有所帮助,因为命令替换删除尾随换行符。它也适用于尾部空行,试试这个看看:x=$(echo foo; echo; echo); echo "$x"

如果除了计数之外还想以其他方式处理 shell 脚本中的行,那么将文本存储在变量中可能不是最好的选择。你可以尝试

for line in $lines ; do 
    something with "$line"
done

但这存在未加引号的变量的常见问题,即文件名通配和空格上的分词。包含“foo bar doo”的一行将被视为三行,因为默认情况下,空格也会分开。

您可能想使用while read循环,但为此,支持进程替换的 shell 可能会更好。看Bash常见问题解答 001find & while 后变量没有改变在 bash 中,在管道未设置值后读取

相关内容