0

我希望我的 perl 程序在所有文件中替换所有文件中的 '{' - > '{function('.counter++.')' ,当同一行中有 '{' 和 '}' 时,并且除非“{”出现在“typedef”子字符串下的一行。

#!/usr/bin/perl

use strict;
use warnings;
use Tie::File;
use File::Find;

my $dir = "C:/test dir";   


# fill up our argument list with file names:
find(sub { if (-f && /\.[hc]$/) { push @ARGV, $File::Find::name } }, $dir);

$^I = ".bak";   # supply backup string to enable in-place edit 

my $counter = 0; 

# now process our files 
while (<>) 
{
    my @lines;
    # copy each line from the text file to the string @lines and add a function call after every '{' '
    tie @lines, 'Tie::File', $ARGV or die "Can't read file: $!\n"

    foreach  (@lines) 
    {   
        if   (!( index (@lines,'}')!= -1 )) # if there is a '}' in the same line don't add the     macro
            {
                s/{/'{function(' . $counter++ . ')'/ge;
                print;
            }

    }
    untie @lines; # free @lines
}    

我试图做的是遍历我在我的目录和子目录中找到的@ARGV 中的所有文件,对于每个 *.c 或 *.h 文件,我想逐行检查并检查此行是否包含'{ '。如果是,程序将不检查是否有'{'并且不会进行替换,如果没有,程序将用'{function('.counter++.');'替换'{'

不幸的是,这段代码不起作用。我很惭愧地说我试图让它整天工作但仍然没有成功。我认为我的问题是我并没有真正使用搜索'{'但我不明白的行为什么。我真的很感激一些帮助。

我还想补充一点,我在 Windows 环境中工作。

谢谢你!!

编辑:到目前为止,在您的帮助下,这是代码:

use strict;
use warnings;
use File::Find;

my $dir = "C:/projects/SW/fw/phy";   # use forward slashes in paths

# fill up our argument list with file names:
find(sub { if (-f && /\.[hc]$/) { push @ARGV, $File::Find::name } }, $dir);

$^I = ".bak";   # supply backup string to enable in-place edit 

my $counter = 0; 

# now process our files
while (<>) {
    s/{/'{ function(' . $counter++ . ')'/ge unless /}/;
    print;
}

剩下要做的就是让它忽略 '{' 替换,当它是 'typedef' 子字符串下的一行时,如下所示:

typedef struct 
{
}examp;

非常感谢您的帮助!谢谢!:)

编辑#2:这是最终代码:

use strict;
use warnings;
use File::Find;

my $dir = "C:/exmp";   

# fill up our argument list with file names:
find(sub { if (-f && /\.[hc]$/) { push @ARGV, $File::Find::name } }, $dir);

$^I = ".bak";   # supply backup string to enable in-place edit 

my $counter = 0; 
my $td = 0;

# now process our files
while (<>) {
    s/{/'{ function(' . $counter++ . ')'/ge if /{[^}]*$/ && $td == 0;
    $td = do { (/typedef/ ? 1 : 0 ) || ( (/=/ ? 1 : 0 ) && (/if/ ? 0 : 1 ) && (/while/ ? 0 : 1 ) &&             (/for/ ? 0 : 1 ) && (/switch/ ? 0 : 1 ) )}; 
    print;
}

代码进行替换,除非替换位置上方的行包含'typedef',当它上面的行包含'='并且没有'if','while','for'或'switch'时,替换也不会发生.

感谢大家的帮助!

4

3 回答 3

3

-iswith 让您为备份文件设置一个扩展名。

使用 perl:

perl -pe "/{[^}]*\$/&&do{s/{/{function('.counter++.');/}" -i.bak *

或(相同的结果):

perl -pe "s/{/{function('.counter++.');/ if /{[^}]*\$/" -i.bak *

对于处理子文件夹中的所有文件,这可能更容易使用find

find . -type f -print0 |
    xargs -0 perl -pe "s/{/{function('.counter++.');/ if /{[^}]*\$/" -i.bak

使用GNU sed让您可以非常快速地完成工作

sed -e "/{[^}]*\$/{s/{/{function('.counter++.');/}" -i.bak *

编辑仅在上一行包含 wordtypedef时进行修改:

perl -pe "BEGIN { my \$td=1; };s/{/{function('.counter++.');/ if /{[^}]*\$/ && \$td==1 ; \$td=do{/typedef/?0:1};"  -i.bak *

可以写;

perl -pe "
BEGIN { my \$td=0; };
s/{/{function('.counter++.');/ if /{[^}]*\$/ && \$td==0 ;
\$td=do{/typedef/?1:0};"  -i.bak *

或更具可读性

perl -pe '
    BEGIN { my $td=0; };
    s/{/{function(\047.counter++.\047);/ if /{[^}]*$/ && $td==0;
    $td=do{/typedef/?1:0};
  ' -i.bak *

或作为 perl 脚本文件:cat >addFunction.pl

#!/usr/bin/perl -pi.bak
BEGIN { my $td = 0; }
s/{/{function(\047.counter++.\047);/ if /{[^}]*$/ && $td == 0;
$td = do { /typedef/ ? 1 : 0 };

解释:

  • BEGIN{...}命令块在程序开始时执行。
  • s/// if // &&如果当前匹配匹配$td=0则替换
  • $td=do{ aaa ? bbb : ccc }assing to td: if aaa then bbb else ccc.

由于 perl 连续运行,$td保持他的值直到下一次分配。因此,如果在分配之前进行替换测试$td,则检查将使用先前的值。

最后,同样使用 sed:

sed -e '/{[^}]*$/{x;/./{x;s/{/{function(\o047.counter++.\o047);/;x;};x;};h;s/^.*typedef.*$//;x;' -i.bak *

或更具可读性:

sed -e '
    /{[^}]*$/{
        x;
        /./{
            x;
            s/{/{function(\o047.counter++.\o047);/;
            x;
        };
        x;
    };
    h;
    s/^/./;
    s/^.*typedef.*$//;
    x;
' -i.bak *

一些sed技巧:

  • h将当前行存储(备份)到保留空间
  • x与保持空间交换当前工作线
  • s///众所周知的替换字符串命令
  • \o047八进制刻度:'
  • /{[^}]*$/{ ... }命令块仅在行 macching{和 no上执行}
  • /./{ ... }仅在包含至少 1 个字符的行上执行的命令块
于 2013-01-15T08:09:12.060 回答
1

如果存在“}”,这是跳过替换的一种方法:

if ( $_ !~ /}/ ) {  # same as !( $_ =~ /}/ )

    s/{/'{function(' . $counter++ . ')'/ge;
}

但请确保print在条件之外,否则如果缺少“}”,则不会打印该行。

其他写法:

unless ( /}/ ) {

    s/{/'{function(' . $counter++ . ')'/ge;
}

或者简单地说:

s/{/'{function(' . $counter++ . ')'/ge unless /}/;
于 2013-01-15T08:03:56.640 回答
0

我认为这个问题是您正在寻找@lines 中的索引。你要这个:

while (<>) 
{
    my @lines;
    # copy each line from the text file to the string @lines and add a function call after every '{' '
    tie @lines, 'Tie::File', $ARGV or die "Can't read file: $!\n"

    foreach  my $line(@lines) 
    {   
        if   ( index ($lines,'}')== -1 ) # if there is a '}' in the same line don't add the     macro
            {
                $line =~ s/{/'{function(' . $counter++ . ')'/ge;
            }
        print $line;


    }
    untie @lines; # free @lines
}

我也有点不清楚 $ARGV 是如何设置的。您可能希望根据脚本的方式在该位置使用 $_ 。

于 2013-01-15T07:35:01.270 回答