【问题标题】:Line by line editing to all the files in a folder ( and subfolders ) in perl在 perl 中逐行编辑文件夹(和子文件夹)中的所有文件
【发布时间】:2013-01-15 07:25:59
【问题描述】:

我希望我的 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' 时,替换也不会发生。

感谢大家的帮助!

【问题讨论】:

  • 为什么不使用 sed? sed -e '/{[^}]*$/{s/{/{function('\''.counter++.'\'');/}' -i.bak *
  • @squiguy 没有人是完美的! ;-) 完全可以,你也可以使用 perl 来做同样的事情……见我的回答。

标签: perl replace


【解决方案1】:

-i 开关可让您为备份文件设置扩展名。

使用 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 *

编辑用于修改仅当上一行包含单词typedef

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 &gt;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 // &amp;&amp; 替换 if 当前匹配匹配 and $td=0
  • $td=do{ aaa ? bbb : ccc } assing to td: if aaa then bbb else ccc.

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

最后,同样使用 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 八进制刻度:'
  • /{[^}]*$/{ ... } 命令块只对匹配{ 的行执行,而不是}
  • /./{ ... } 仅对包含至少 1 个字符的行执行的命令块

【讨论】:

  • 谢谢,但我还需要为替换添加另一个例外,即如果“{”位于包含子字符串“typedef”的行下。在这种情况下,我需要逐行编辑不是吗?
  • 不,perl(如 sed)按顺序工作,因此您可以存储以前的条件...请编辑您的问题!
  • 我编辑了这个问题。谢谢。你有解决我的问题的方法吗?非常感谢您的帮助!
  • 我想我可能做错了什么。您建议作为整个代码的行。我在哪里写?如果我尝试运行它,它会给我一堆错误。
  • 还是不行。我想这与我使用的是 windows 环境有关......
【解决方案2】:

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

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

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

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

其他写法:

unless ( /}/ ) {

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

或者简单地说:

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

【讨论】:

  • 哇!!完美的 !我使用了你的第三个建议,它就像一个魅力。 O 现在唯一需要做的是了解如何让程序跳过对 '{' 的替换,如果它是 'typedef' 下的一行。例如当有 typedef struct \n { \n }examp;必须没有替代品
  • @MiSo :这与您最初的要求相比有很大的变化。我建议您不要修改这个问题,而是打开一个新问题
【解决方案3】:

我认为这个问题是您正在寻找@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 是如何设置的。您可能希望根据脚本的方式在该位置使用 $_。

【讨论】:

  • 感谢您的回复。出于某种原因,如果我按照您的建议进行操作,它会删除所有文件的所有内容。
  • 那是因为如果没有匹配到我忘记打印了。我还在 if 语句中留下了一些额外的括号。立即尝试。
  • 它有这个错误:全局符号“@lines”需要在文件名中明确的包名全局符号“$line”需要在文件名中明确的包名
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-08
  • 1970-01-01
  • 2020-07-02
  • 2021-04-29
  • 2016-02-26
  • 1970-01-01
相关资源
最近更新 更多