POSIX 方式在文件中的一行周围添加内容

POSIX 方式在文件中的一行周围添加内容

我正在寻找一种使用脚本编辑文件的方法,该脚本将为 C 中的变量提供替代定义,并且此重新定义由使用我为其编写脚本的 POSIX 应用程序的编译器 D 标志控制。例如,假设令牌nice_var应该重命名为cool_var

输入示例:

struct BigThing nice_var;

/* some lines later */

    nice_var->thing = 1;

预期输出:

/* TODO temporary ifdef to facilitate renaming */
#ifdef RENAME_FEATURE_ENABLED
struct BigThing cool_var;
#else
struct BigThing nice_var;
#endif /* RENAME_FEATURE_ENABLED */

/* some lines later */

/* TODO temporary ifdef to facilitate renaming */
#ifdef RENAME_FEATURE_ENABLED
    cool_var->thing = 1;
#else
    nice_var->thing = 1;
#endif /* RENAME_FEATURE_ENABLED */

字符串的每次出现都应封装在所示的预处理器指令模式中,即使用法出现在连续行上也是如此。

是的,我可以编写一个 python/perl/javascript 脚本来执行此操作,但是是否有 POSIX 应用程序可以支持这种文件编辑?我期望edex能够做到这一点,但我不知道如何循环ed以解决文件中的所有出现情况,也不知道如何检测ex搜索何时到达文件末尾。有关如何检测这一点的信息ex可能就是我实现目标所需的全部信息。

如果不明显,我计划使用该脚本(我将其称为renamer)来编辑工作区中出现“nice_var”的所有文件,如下所示:

#!/bin/bash -eux
for file in $(grep --files-with-matches --recursive 'nice_var'); do
    renamer --feature-name RENAME_FEATURE_ENABLED --var-name 'nice_var' --replacement 'cool_var' "${file}"
done

无需处理答案中的命令行参数。我对在匹配的行之前添加内容以及复制该行的概念更感兴趣。

问题实际上归结为:“是否有一个 POSIX 应用程序支持重复搜索模式,然后在找到模式的行之前和之后添加行,其中添加的行之一与模式匹配,除了图案被替换了?”

也许awk有多次通过?

答案1

这是一个“尽力而为”的解决方案,它提供了一种简单的替换,无需了解语言语法,无论文本是变量、注释行 ( /* don't change nice_var here */)、字符串中的引号 ( char *p = "I've got nice_var here"),甚至是另一个单词的一部分 ( int quitenice_vartoo) 。您需要一个 C 语言解析器的近似值才能正确正确地实现这一点。

awk -v src=nice_var -v dst=cool_var -v macro=RENAME_FEATURE_ENABLED '
    $0 ~ src {
        this = that = $0;
        gsub(src, dst, that);
        printf "/* TODO temporary ifdef to facilitate renaming */\n#ifdef %s\n%s\n#else\n%s\n#endif /* %s */\n", macro, that, this, macro;
        next
    }
    1
' sourcefile.c

您的示例的输出:

/* TODO temporary ifdef to facilitate renaming */
#ifdef RENAME_FEATURE_ENABLED
struct BigThing cool_var;
#else
struct BigThing nice_var;
#endif /* RENAME_FEATURE_ENABLED */

/* some lines later */

/* TODO temporary ifdef to facilitate renaming */
#ifdef RENAME_FEATURE_ENABLED
    cool_var->thing = 1;
#else
    nice_var->thing = 1;
#endif /* RENAME_FEATURE_ENABLED */

请注意,这实际上是用于匹配替换文本的src正则表达式。awk我的示例是字符串文字,但不一定如此。

答案2

使用sed

/nice_var/!b
h
i\
/* TODO temporary ifdef to facilitate renaming */\
#ifdef RENAME_FEATURE_ENABLED
s//cool_var/gp
i\
#else
g
a\
#endif /* RENAME_FEATURE_ENABLED */

忽略不匹配的行nice_var(分支到每个此类行的脚本末尾)。对于匹配的行nice_var,用 将该行存储在保留空间中h,插入“前缀”( #ifdef ...),更改nice_varcool_var并打印修改后的行,插入“中缀”( #else),从保留空间中取出原始行g(这不实际上并不打印它,隐式打印在最后执行此操作),并附加“后缀”(#endif)。

测试:

$ cat file.c
struct BigThing nice_var;

/* some lines later */

    nice_var->thing = 1;
$ sed -f script file.c
/* TODO temporary ifdef to facilitate renaming */
#ifdef RENAME_FEATURE_ENABLED
struct BigThing cool_var;
#else
struct BigThing nice_var;
#endif /* RENAME_FEATURE_ENABLED */

/* some lines later */

/* TODO temporary ifdef to facilitate renaming */
#ifdef RENAME_FEATURE_ENABLED
    cool_var->thing = 1;
#else
    nice_var->thing = 1;
#endif /* RENAME_FEATURE_ENABLED */

要参数化sed脚本,请使用此处文档。请注意,这要求第一个参数是适合用作sed替换中的模式的 POSIX 基本正则表达式,第二个参数是适合替换命令的替换部分的文本字符串。第三个参数必须是有效的 C 预处理器宏名称。

src=$1
dst=$2
mac=$3

cat >script <<SED_SCRIPT
/$src/!b
h
i\\
/* TODO temporary ifdef to facilitate renaming */\\
#ifdef $mac
s//$dst/gp
i\\
#else
g
a\\
#endif /* $mac */
SED_SCRIPT

sed -f script

测试:

$ sh replacer nice cool HELLO <file.c
/* TODO temporary ifdef to facilitate renaming */
#ifdef HELLO
struct BigThing cool_var;
#else
struct BigThing nice_var;
#endif /* HELLO */

/* some lines later */

/* TODO temporary ifdef to facilitate renaming */
#ifdef HELLO
    cool_var->thing = 1;
#else
    nice_var->thing = 1;
#endif /* HELLO */
$ sh replacer 'nice_\(var\)' '\1iable' HELLO <file.c
/* TODO temporary ifdef to facilitate renaming */
#ifdef HELLO
struct BigThing variable;
#else
struct BigThing nice_var;
#endif /* HELLO */

/* some lines later */

/* TODO temporary ifdef to facilitate renaming */
#ifdef HELLO
    variable->thing = 1;
#else
    nice_var->thing = 1;
#endif /* HELLO */

相关内容