在一个命令行语句中 Grep 2 个不同的要求

在一个命令行语句中 Grep 2 个不同的要求

如何打印包含该字符串的最后 3 行#includetester.c如果包含该字符串的行少于 3 行,则打印整个文件

到目前为止我已经:

grep "#include" tester.c | tail -3

但我不知道如何包含要求的后半部分。这是家庭作业,解决方案必须是一行,没有,;这就是为什么我似乎无法弄清楚。

答案1

tail -3不会打印超过 3 行;它打印 3 个或更少。因此,如果 'grep' 找到 3 个或更少的匹配行,tail 会将它们全部传递到标准输出。如果匹配项超过 3 个,tail则将其限制为仅 3 行。

您甚至在将问题发布到此处之前就已经解决了您的问题,因为您的命令正是您所需要的......

这就是你晚上工作时会发生的事情:)

编辑:我可能误读了你的问题..

如果您想在找到 <3 个匹配项时打印整个文件 - 只需运行

f="testfile.c" && C=3 && [ $(grep -c "#include" ${f}) -lt ${C} ] && cat ${f} || grep "#include" ${f} |tail -${C}

但是,如果您正在处理可能较慢的 FS(例如 NFS),或者您正在处理具有大量行或行非常长的文件,那么这对您来说可能太慢了,因为您必须读取该文件两次。

f="testfile.c" && C=3 && CONTENT=$(cat "${f}") && MATCHES=$(echo "$CONTENT"|grep "#include"|tail -${C}) && [ $(echo "$MATCHES" | wc -l) -lt ${C} ] && echo "$CONTENT" || echo "$MATCHES"

这样,您只需打开并读取文件一次,但您会将其缓冲在 RAM 中(这比存储设备快得多)。如果您的 RAM 不足或缓冲文件对您来说太昂贵,请使用第一个命令 [其中文件被打开两次];它速度较慢,但​​不会使操作系统或您的应用程序崩溃(尽管进程陷入“D”状态的风险更高......而且这对操作系统也没有好处......)

答案2

你可以像 Sildoreth 所说的那样做,不需要“;”像这样:

test $(echo `grep "#include" tester.c` | wc -l) -ge 3 && {echo `grep "#include" tester.c` | head -n 3} || cat tester.c

答案3

这是一个奇怪的问题,特别是考虑到您在原始帖子中提出的命令正是您想要的?

例如,在 file.c 中,有 15 行与 grep 表达式匹配:

% grep -c "#include" file.c 15 % grep "#include" file.c | head -18 #(more than the number of matches) #include <sys/blah.h> #include <sys/blah2.h> #include <sys/blah3.h> #include <sys/blah4.h> #include <sys/blah5.h> #include <sys/blah6.h> #include <sys/blah7.h> #include <sys/blah8.h> #include <sys/blah9.h> #include <sys/blah10.h> #include <sys/blah11.h> #include <sys/blah12.h> #include <sys/blah13.h> #include <sys/blah14.h> #include <sys/blah15.h>

或者

% grep "#include" file.c | tail -18 #include <sys/blah.h> #include <sys/blah2.h> #include <sys/blah3.h> #include <sys/blah4.h> #include <sys/blah5.h> #include <sys/blah6.h> #include <sys/blah7.h> #include <sys/blah8.h> #include <sys/blah9.h> #include <sys/blah10.h> #include <sys/blah11.h> #include <sys/blah12.h> #include <sys/blah13.h> #include <sys/blah14.h> #include <sys/blah15.h>

请解释一下这为什么不符合您的作业参数?

答案4

尝试这个:

lines=$(grep "#include" tester.c);
   test $(echo $lines | wc -l) -ge 3 && {echo $lines | head -n 3} || cat tester.c;
       unset lines

注意:此解决方案可以键入一行。我将其分成多行只是为了便于阅读。

编辑

作为对评论的回应,我想您可以将分号替换&&

lines=$(grep "#include" tester.c) &&
   test $(echo $lines | wc -l) -ge 3 && {echo $lines | head -n 3} || cat tester.c

这在技术上并没有错,但它是lines固定的。这也是&&不必要的,但没关系。

此修改后的解决方案仍然具有优点,即您不必像其他一些解决方案那样读取文件两次。

如果您不能使用test,您可以使用括号,如下所示:

lines=$(grep "#include" tester.c) &&
   [ $(echo $lines | wc -l) -ge 3 ] && {echo $lines | head -n 3} || cat tester.c

您甚至可以删除花括号:

lines=$(grep "#include" tester.c) &&
   [ $(echo $lines | wc -l) -ge 3 ] && echo $lines | head -n 3 || cat tester.c

这些变体之一应该可以满足您的要求。

相关内容