0

同じ行に '{' と '}' がある行を除くすべてのファイルで、perl プログラムに '{' - > '{function('.counter++.')' の置換を実行させたいのですが、 '{' が 'typedef' 部分文字列の 1 行下にある場合を除きます。

#!/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」サブストリングの下の 1 行である場合に「{」置換を無視するようにすることです。

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 *

編集前の行に単語が含まれていない場合にのみ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 >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 }tdへの評価:aaaの場合はbbb、それ以外の場合は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///よく知られている置換文字列コマンド
  • \o0478進数の目盛り:'
  • /{[^}]*$/{ ... }{の機械加工でのみ実行するコマンドブロック}
  • /./{ ... }少なくとも1文字を含む行でのみ実行するコマンドブロック
于 2013-01-15T08:09:12.060 に答える
1

'}'が存在する場合に置換をスキップする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 に答える