【问题标题】:Weird, unexpected output in Bash Script with Perl带有 Perl 的 Bash 脚本中奇怪的、意外的输出
【发布时间】:2015-09-06 06:26:12
【问题描述】:

我正在尝试编写一个 Bash 脚本,该脚本将在包含多个 C 源文件的文件夹中运行。其中之一将包含 main(),并且还将在顶部包含一个多行注释,用于描述文件/程序及其功能。

到目前为止我所拥有的是:

#! /bin/bash
echo "Grepping for main *.c file"
file=$(grep -l main *.c)
echo "the file is ${file}"
comment=$(perl -lne 'print if (/\/\*/ .. /\*\//)' ${file})
echo ${comment}

它告诉我哪个文件包含 main()(在测试用例中,它是 main.c)。然后 perl 函数错误地输出(我认为是)。它不只是在文件中搜索,而是输出

Grepping for main *.c file
the file is main.c
/$RECYCLE.BIN /Applications /Backup /Extra /Extra (from old Mac) 2 /Library /Network /OS X Install Data /System /System Volume Information /Users /Volumes /bin /boot CMakeLists.txt build comment.sh main.c main.dSYM makefile tasksheet.pdf Student Number: n9999999 CMakeLists.txt a1_n9999999 build comment.sh main.c main.dSYM makefile tasksheet.pdf...

后面是文件夹内容的标题,实际的多行注释分散在整个输出中。

如果我尝试 cat main.c 它会正确输出,但如果在 bash 脚本中我回显 cat main.ccat ${file} 我会得到类似类型的垃圾输出。是什么原因造成的?我感觉这与多行注释包含 /* 的事实有关,在 Unix 中它是“根目录中的所有内容”,但是有没有办法解决这个问题?

编辑:在终端中运行该 perl 命令 perl -lne 'print if (/\/\*/ .. /\*\//)' main.c 会输出预期的结果。

【问题讨论】:

  • 脚本中的 cat main.c 给出的结果与命令行中的 cat main.c 不同?如果是这样,那么这里就很不对劲了;我怀疑问题中缺少信息。
  • @ash Davids-Mac-Pro:Assignment 1 David$ cat main.c /* ** Student Number: n90000000 ** Name: David ** Extra functionality: ** List of functionality. */ // Standard Includes #include <stdio.h> #include <stdbool.h> #include <string.h> 使用包含 echo $(cat main.c) 的 bash 脚本:/$RECYCLE.BIN /Applications /Backup (redacted) CMakeLists.txt a1_n9999999 build comment.sh main.c main.dSYM makefile tasksheet.pdf Student Number: (redacted) List of functionality. build/ main.dSYM/ // Standard Includes #include <stdio.h> #include <stdbool.h> #include <string.h>
  • 请注意,main 的测试不够敏感。将匹配包含单词remaining 或包含字母序列m-a-i-n 的许多其他单词中的任何一个的文件。然而,这在很大程度上与手头的问题无关。
  • @JonathanLeffler 我不太擅长 grep。关于如何使测试更健壮的任何提示?
  • 最基本的步骤是grep -l -w main 来搜索“main”这个词。但是,这会在评论中选择“主要思想是……”等等。我可能会利用有关如何格式化main() 函数的知识,使用grep -l -E '^int main\((void|int argc, char \*\*argv)\)$ 来获取int main(void)int main(int argc, char **argv) — 它们是我使用过的唯一签名,并且我保持类型不变线作为函数。如果您将int 放在上一行,请从匹配项中删除int。您可能仍会遇到“评论中的主要内容”问题,但是……

标签: bash perl shell


【解决方案1】:

你需要双引号回声:

echo "${comment}"

您会看到 /* 扩展到根目录中的文件列表中。

(你的根目录中有很多不应该存在的碎片。你不应该在/ 目录中写入文件。你需要担心你是如何(错误地)使用root特权。)


请注意,main 的测试不够灵敏。将匹配包含单词剩余或包含字母序列 m-a-i-n 的许多其他单词中的任何一个的文件。但是,这在很大程度上与手头的问题无关。

我不太擅长 grep。关于如何使测试更健壮的任何提示?

最基本的步骤是grep -l -w main 搜索“main”这个词。但是,这会在评论中选择“主要思想是……”等等。我可能会利用有关如何格式化main() 函数的知识,使用grep -l -E '^int main\((void|int argc, char \*\*argv)\)$ 来获取int main(void)int main(int argc, char **argv) — 它们是我使用过的唯一签名,并且我保持类型不变线作为函数。如果您将int 放在上一行,则将int 从匹配项中删除;如果您在同一行有左大括号,请修改模式以允许或要求该布局;等等。您可能仍然会遇到“评论中的主要内容”问题,但这不太可能。

我还注意到 Perl 会提取每个文件中的每个块注释,而不是在第一个块的末尾停止。大多数源文件中不应该有一个注释。我认为您可以通过以下方式解决此问题:

perl -ne 'if (/\/\*/ .. /\*\//) { print if ($comment == 0); } ++$comment if m/\*\//;'

我尝试了一个更紧凑的符号:

perl -lne 'print if (/\/\*/ .. /\*\// && $comment == 0); ++$comment if m/\*\//'

这个主题的一些变体和&& $comment == 0 条件在功能上被忽略了。上面显示的替代方法有效。


好奇心战胜了我。使用“Deparse”模块(SO 搜索“[perl] deparse”)并应用它显示:

$ perl -MO=Deparse -lne 'print if (/\/\*/ .. /\*\// && $comment == 0); ++$comment if m/\*\//'
BEGIN { $/ = "\n"; $\ = "\n"; }
LINE: while (defined($_ = <ARGV>)) {
    chomp $_;
    print $_ if m[/\*] .. m[\*/] && $comment == 0;
    ++$comment if m[\*/];
}
-e syntax OK
$ perl -MO=Deparse -lne 'print if ((/\/\*/ .. /\*\//) && $comment == 0); ++$comment if m/\*\//'
BEGIN { $/ = "\n"; $\ = "\n"; }
LINE: while (defined($_ = <ARGV>)) {
    chomp $_;
    print $_ if m[/\*] .. m[\*/] and $comment == 0;
    ++$comment if m[\*/];
}
-e syntax OK
$ perl -lne 'print if ((/\/\*/ .. /\*\//) && $comment == 0); ++$comment if m/\*\//'  e2big.c
/* SO 18559403: How big an argument list is allowed */
$

&amp;&amp;的优先级高; and 的值很低。如果您使用范围运算符 ..,请小心并在正确的位置使用大量括号。

【讨论】:

  • 这修复了它。我会尽快接受它作为答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-10-17
  • 2020-07-20
  • 1970-01-01
  • 1970-01-01
  • 2011-09-27
  • 2017-11-21
  • 1970-01-01
相关资源
最近更新 更多