将 sed 脚本拉入 perl 程序

Fro*_*otz 2 perl sed pdp-10

我需要缩短函数名称,因此我识别它们并生成一个非常长的 sed 脚本,如下所示:

s/\breally_long_function_name1\b/A00128/g
s/\breally_long_function_name2\b/A00060/g
s/\breally_long_function_name3\b/A00035/g
s/\breally_long_function_name4\b/A00342/g
s/\breally_long_function_name5\b/A00203/g
...
Run Code Online (Sandbox Code Playgroud)

然后这样称呼它:

`sed -i.bak -f $sedscript *`
Run Code Online (Sandbox Code Playgroud)

问题是我不能依赖sed能够处理这些实例\b,而且看起来很混乱。我不想写出 sed 脚本,而是想将其放入一个数组中,然后对需要处理的每个文件的每一行执行类似的操作:

$targetline =~ $processing;
Run Code Online (Sandbox Code Playgroud)

我的问题是,这样使用$processing是行不通的,而且q操作员似乎也没有完成这项工作。我如何处理它以处理替换$processing并输入结果$targetline

注意:我在脚本中使用了 Perl sed 文件以及@ron-bergin 的答案来达到这一点。

其应用是将现代 C 代码转换为可以由古代 C 编译器编译的形式。现有脚本位于https://gitlab.com/DavidGriffith/frotz/-/blob/master/src/misc/snavig.pl,用于准备供 KCC(PDP-10 的早期 C 编译器)编译的源代码大型机。它的怪癖之一是某些符号的长度限制为 6 个字符。有关此内容的背景位于https://github.com/PDP-10/panda/blob/master/files/kcc-6/kcc/user.doc#L519

这是处理前源文件的片段:

void reset_memory(void)
{
        if (story_fp != NULL)
                fclose(story_fp);
        story_fp = NULL;

        if (undo_diff) {
                free_undo(undo_count);
                zfree(undo_diff);
                zfree(prev_zmp);
        }

        undo_diff = NULL;
        undo_count = 0;
        prev_zmp = NULL;

        if (zmp)
                zfree(zmp);
        zmp = NULL;
} /* reset_memory */
Run Code Online (Sandbox Code Playgroud)

处理后,看起来像这样:

void A00156(void)
{
        if (A00144 != NULL)
                fclose(A00144);
        A00144 = NULL;

        if (undo_diff) {
                A00155(A00148);
                zfree(undo_diff);
                zfree(A00147);
        }

        undo_diff = NULL;
        A00148 = 0;
        A00147 = NULL;

        if (zmp)
                zfree(zmp);
        zmp = NULL;
} /* A00156 */
Run Code Online (Sandbox Code Playgroud)

经过这样的处理后,它被证明可以使用 KCC 进行编译,并在仿真和真实 PDP-10 硬件上的 TOPS20 上运行。

当我尝试不使用外部 sed,而不是做任何事情时,我什么也没得到,然后在按 ^C 时出现大量这样的信息:

Use of uninitialized value $targetline in pattern match (m//) at src/misc/snavig.pl line 181, <$targetfile> line 104.
Use of uninitialized value $targetline in pattern match (m//) at src/misc/snavig.pl line 181, <$targetfile> line 104.
Use of uninitialized value $targetline in pattern match (m//) at src/misc/snavig.pl line 181, <$targetfile> line 104.
Use of uninitialized value $targetline in pattern match (m//) at src/misc/snavig.pl line 181, <$targetfile> line 104.
Run Code Online (Sandbox Code Playgroud)

zdi*_*dim 7

组织大量替换的一种方法

\n
use warnings;\nuse strict;\nuse feature \'say\';\n\nmy %repl = ( \n    really_long_function_name1 => \'A00128\',\n    really_long_function_name2 => \'A00060\',\n    # ...\n);\n\nmy $re = join \'|\', keys %repl;  # add quotemeta if needed. see text\n\nwhile (<>) { \n    s/\\b($re)\\b/$repl{$1}/g;\n    print\n}\n
Run Code Online (Sandbox Code Playgroud)\n

操作<>逐行读取命令行上给出的名称的文件。每行,无论是否更改,都只是打印出来,因此它充当过滤器。如果需要就地编辑文件,则需要为此调整代码。\xe2\x80\xa0

\n

如果模式中使用的任何键可以具有正则表达式专用的符号,则应将它们转义,并且该工具是quotemeta --\njoin \'|\', map { quotemeta } keys %repl。但这里的键是 C 程序中的函数名称。

\n

这并不能解决某些问题(如果某些替换包含在其他替换中怎么办?),并且可能需要根据细节进行其他调整。我不太明白所有要点,特别是为什么将替换列表打印到文件中。如果这很重要,则可以从具有方便格式的文件中读取上面的替换对(Perl 数据结构的转储??JSON或者YAML使其也易于阅读?)

\n

通过向散列添加替换对,可以轻松编辑/扩展该列表。

\n
\n

\xe2\x80\xa0问题中链接的 SO 页面显示了一种方法:设置$^I 全局变量(开关的值-i)。由于它是一个空字符串,输入文件被“就地”更改,但我们没有得到备份,否则它的值将作为后缀添加到备份文件中

\n
local $^I = \'\';   # changes made to input files. no backup \n\nwhile (<>) { \n    s/\\b($re)\\b/$repl{$1}/g;\n    print;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

或者

\n
local $^I = \'.bak\';  # added suffix for the backup file(s)\n\nwhile (<>) { \n    s/.../.../g;\n    print;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

确保此代码处于足够小的范围内,以便能够使用local来限制此更改 - 以便解释器的其余部分不受影响!

\n

或者,如果感觉不好,请手动处理文件列表。一旦程序被调用

\n
progname [options]  file1 file2...\n
Run Code Online (Sandbox Code Playgroud)\n

那么在运行的程序中,数组@ARGV包含命令行中的所有单词(程序名称除外)。

\n

由破折号选项(带有或)@ARGV处理的内容将被删除,保留的内容是, (等)文件名。所以在处理选项之后你可以做Getopt::Long---@ARGVfile1file2Getopt::Long

\n
foreach my $filename (@ARGV) { \n    # handle the file $filename\n}\n
Run Code Online (Sandbox Code Playgroud)\n

或者,将文件名从其@ARGV自己的数组中安全地复制并从那里进行处理。(或者,可以将文件名作为选项的一部分,以便将它们提取出来Getopt::Long。)

\n

如果您处理这样的文件(而不是让“钻石”运算符<>执行此操作),那么还有一些库可以就地更改文件,例如Path::Tiny::edit_lines

\n
use Path::Tiny;\n\npath($filename)->edit_lines( sub { s/\\b($re)\\b/$repl{$1}/g } );\n
Run Code Online (Sandbox Code Playgroud)\n