假设我有文件 main.txt,其中包含以下内容:
一些文字
#包含片段1.txt
其他一些文字
#包括fragment2.txt”
其他一些文字
其中fragment1.txt 和fragment2.txt 如下所示:
片段1.txt:
这是fragment1的内容
片段2.txt:
这是fragment2的内容
问题:如何显示带有“扩展”包含文件的整个 main.txt 文件,如下所示:
一些文字
这是fragment1的内容
其他一些文字
这是fragment2的内容
其他一些文字
谢谢
答案1
如果您要使用#include "file"
C 编程语言中的语法,您可以使用cpp
它来解析它。比如说,如果你main.txt
看起来像这样:
Some text
#include "fragment1.txt"
Some other text
#include "fragment2.txt"
Some other text
你可以这样做:
$ cpp -nostdinc -P <main.txt
Some text
This is content of fragment1
Some other text
This is content of fragment2
Some other text
正如中所解释的man cpp
:
-nostdinc 不要在标准系统目录中搜索头文件。仅搜索使用 -I 选项指定的目录(以及当前文件的目录,如果适用)。
-P 禁止在预处理器的输出中生成行标记。当在非 C 代码上运行预处理器时,这可能很有用,并且将被发送到可能被行标记混淆的程序。
但请注意,这cpp
不仅仅是解析#include
行,并且根据您的用例,它可能有用也可能没用。例如,cpp
自动删除所有 C 风格注释:
// this is a comment
/* this as a comment too /*
它看起来很有用,如果您想保留评论,可以使用-C
选项。另一件事是,它将cpp
尝试解释所有以#
预处理器指令开头的行。例如这个:
#define A 7
Value: A
将打印为:
Value: 7
和这个
#blah blah
会抛出错误:
<stdin>:14:2: error: invalid preprocessing directive #blah
在实践中,有一些程序依赖cpp
作为其配置解析器,例如xrdb
.
答案2
一个小脚本就可以解决字面量#include
,
不能递归,包含文件本身会造成死循环
并且递归包含没有按要求说明。
最终可能需要一个真正的解析器,你可以参考其他答案。
通过标准输入输入main.txt
以下 perl 脚本,
输出是(大致)所需的输出。
awk 也可以完成这项工作,但我喜欢 Perl 的神秘感:)
#!/usr/bin/perl
while (<>) {
if (/^#include (.*)/) {
system('cat',$1);
} else {
print;
}
}
上述代码的更短的单行版本:
perl -e '(/^#include (.*)/ ? system("cat",$1) : print) while <>;' <main.txt
对于那些可能有兴趣了解更多 Perl 的人:
<>
是 read from 的缩写STDIN
,它是标准输入流关键字
while
可以放在行尾;()
当没有歧义的解释时可以省略。对于从 读取的每一行
STDIN
,perl 将其存储在$_
$_
是一个预定义变量,大多数 perl 内置函数在未给出参数的情况下接受该变量作为参数,print
是接受的内置函数之一$_
读取每一行后,perl 将计算三元表达式。正则表达式匹配
$_
默认适用当
/^#include (.*)/
返回1(检测到的匹配数)时,表示$_
以文字开始#include
$_
perl 存储to的剩余部分$1
,因为它是第一个分组捕获($2,$3...如果有更多组则定义)该
system
函数通过系统 shell 调用命令,主要是sh
在这一行中调用cat
,并$1
作为其参数传递。当 ,
/^#include (.*)/
上返回 0时$_
,print
会被评估并作为副作用,它会打印出来$_
三元表达式的值被默默忽略
答案3
直接运行C预处理器:
$ cpp -P main.txt
这样就可以了。不过,您需要引用包含文件的名称:
#include“fragment1.txt”
作为奖励,您可以获得 C 预处理器的全部功能:宏、条件包含等。
答案4
使用 GNU sed
,您可以在命令中使用该e
标志s
:
sed 's/^#include/cat/e'
替换#include
为cat
并执行它,因此该行被命令的答案替换cat
。
如果文件名可能包含空格等字符,最好引用它:
sed 's/^#include *\(.*\)/cat "\1"/e'
但请注意,对于带有反斜杠、勾号和其他一些特殊文件名,这仍然可能会失败。