总的来说,您应该遵循 Charles Duffy 的建议,使用更合适的工具来完成任务。但我想回顾一下为什么当前脚本不起作用以及如何修复它,作为一个学习练习。
另外,关于 shell 脚本检查和故障排除的两个快速建议:通过shellcheck.net 运行脚本以指出常见错误,调试时将set -x 放在问题部分之前(set +x 之后),这样 shell将在脚本运行时打印出它认为正在发生的事情。
问题在于您使用file 变量的方式。让我们看看这是做什么的:
for file in "$(ls | grep .*.c)"
首先,ls 打印当前目录中的文件列表,每行一个。 ls 确实是为交互使用而设计的,它的输出可能是模棱两可的,很难正确解析;在脚本中,几乎总是有better ways to get lists of filenames(稍后我会告诉你一个)。
ls 的输出通过管道传送到grep .*.c,这在很多方面都是错误的。首先,由于该模式包含通配符(“*”),shell 将尝试将其扩展为匹配文件名列表。如果该目录包含任何隐藏的(带有前导“.”).c 文件,它将用这些文件的列表替换它,并且什么都不会正常工作。 总是将模式参数引用到grep 以防止这种情况发生。
但是模式本身(“.*.c”)也是错误的;它搜索任意数量的任意字符(“.*”),然后是单个任意字符(“.”——这是在正则表达式中,因此“.”不按字面意思处理),然后是一个“c”。它会在行中的任何位置搜索这个,因此任何包含“c”的文件名都将匹配。您想要的模式类似于'[.]c$'(请注意,我将其包裹在单引号中,因此shell 不会尝试将$ 视为变量引用,就像在双引号中那样)。
还有另一个问题,这是您实际遇到的(部分)问题:ls | grep 的输出被扩展为双引号。它周围的双引号告诉外壳程序不要对结果执行通常的单词拆分和通配符扩展操作。这里常见的(但仍然是错误的)事情是去掉双引号,因为分词可能会将文件名列表分解为单独的文件名,因此您可以一个接一个地迭代它们。 (除非任何文件名包含有趣的字符,在这种情况下它会产生奇怪的结果。)但是使用双引号它不会拆分它们,它只是将整个东西视为一个大项目,所以你的循环运行一次 file设置为“src1.c\nsrc2.c\nsrc3.c”(\n 代表实际的换行符)。
这是解析ls 时可能遇到的麻烦。不用,直接用shell通配符即可:
for file in *.c
这要简单得多,避免了所有关于正则表达式模式语法与通配符模式语法的混淆、ls 的输出中的歧义等。它简单、清晰,并且可以正常工作。
这可能足以让它为你工作,但如果你正在做这样的事情,还有一些其他的事情你真的应该解决。首先,您应该用双引号引用变量引用(即使用"$file" 而不仅仅是$file)。这是您遇到的错误的另一部分;看第二个awk 命令:
awk -v nomeprog=$file 'BEGIN{FS="(";printf "the file e %s with the open call:", nameprog}//{ print $2}'
将file 设置为“src1.c\nsrc2.c\nsrc3.c”,shell 将对其进行分词和通配符扩展,给出:
awk -v nomeprog=src1.c src2.c src3.c 'BEGIN{FS="(";printf "the file e %s with the open call:", nameprog}//{ print $2}'
awk 将因此将其nomeprog 变量设置为“src1.c”,然后尝试将“src2.c”作为 awk 命令运行(在名为“src3.c”和“BEGIN{FS =...”)。 “src2.c”当然不是有效的 awk 命令,所以会出现语法错误。
这种混乱是未加引号的变量引用可能导致的典型混乱。双引号你的变量引用。
另一件不太重要的事情是你有一个useless use of cat。只要你有模式:
cat somefile | somecommand
(它只是一个文件,而不是几个需要一起catenated),你应该只使用:
somecommand <somefile
在某些情况下,例如awk 和grep,命令本身可以直接将输入文件名作为参数,因此您可以使用:
somecommand somefile
在你的情况下,而不是
cat "$file" | awk '/.*open/{print $0}' | awk -v nomeprog="$file" 'BEGIN{FS="(";printf "the file e %s with the open call:", nameprog}//{ print $2}'
我只是使用:
awk '/.*open/{print $0}' "$file" | awk -v nomeprog="$file" 'BEGIN{FS="(";printf "the file e %s with the open call:", nameprog}//{ print $2}'
(尽管正如 Charles Duffy 指出的那样,即使 也可以简化很多。)