Как изменить все вхождения символа во всех файлах в каталоге (и подкаталогах) с помощью Perl

** У меня есть следующий вопрос, который отмечен знаком * * **

I was asked to write Perl code that replaces every { with {function() and in every replacement the counter should get larger by 1. e.g. first replacement of { will be {function(0) , second replacement of { will be {function(1) etc. It suppose to do the replacement in every *.c and *.h file in a folder including subfolders.

Я написал этот код:

#!/usr/bin/perl
use Tie::File;
use File::Find;
$counter = 0;
$flag = 1;

@directories_to_search = 'd:\testing perl';
@newString = '{ function('.$counter.')';
$refChar = "{";
finddepth(\&fileMode, @directories_to_search);


sub fileMode 
{
 my @files = <*[ch]>;   # get all files ending in .c or .h
 foreach $file (@files) # go through all the .c and .h flies in the directory  
{
    if (-f $file)  # check if it is a file or dir
    {
    my @lines;
# copy each line from the text file to the string @lines and add a function call after every     '{' '
    tie @lines, 'Tie::File', $file or die "Can't read file: $!\n";
    foreach ( @lines )
    {
        if (s/{/@newString/g)
        {
            $counter++;
            @newString = '{function('.$counter.')';
        }
        untie @lines; # free @lines
    }
    }
}   
}

The code searches the directory d:\testing Perl and does the replacement but instead of getting {function() I get {function(number1) function(number3) function(number5) function(number7) for instance for the first replacement I get {function(0) function(2) function(4) function(6) and I wanted to get {function(0)

Я действительно не знаю, что не так с моим кодом.

Решение awk или любое другое решение Perl также будет замечательным!


* I have a follow-up question. now I want my perl program to do the same substitution in all the files except the lines when there is a '{'
and a '}' in the same line. so i modified the code this way.

#!/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
#foreach $filename (@ARGV) 
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";
    #$_='{function(' . $counter++ . ')';

    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 ();'

к сожалению, этот код не работает. Мне стыдно сказать, что я пытаюсь заставить его работать весь день, и все равно не уйти. Я бы очень признателен за помощь.

Спасибо!!

1
nl ja de
Почему вы используете как File :: Find , так и glob для поиска файлов? Знаете, это делает ту же работу дважды.
добавлено автор TLP, источник
Я не вижу, что вы добавляете строку «число» ко всему, что содержится в вашем коде. Вы уверены, что вы имеете в виду, что вы видите «число1», «число3» и т. Д. В вашем выходе?
добавлено автор TLP, источник

4 ответы

Это простой способ объединить метод поиска с редактированием на месте. Вы можете использовать Tie :: File , но это действительно тот же конечный результат. Кроме того, само собой разумеется, вы должны всегда сохранять резервные копии своих исходных файлов при выполнении таких изменений, поскольку изменения необратимы.

Итак, если вам не нужна рекурсия, ваша задача в стиле Unix/Linux проста:

perl -pi -we 's/{/"{ function(" . $i++ . ")"/ge' *.h *.c

Конечно, поскольку вы, кажется, используете Windows, оболочка cmd не будет глотать наши аргументы, поэтому нам нужно сделать это вручную. И нам нужно изменить котировки. Кроме того, нам нужно предоставить аргумент резервного копирования для переключателя -i (in-place edit).

perl -pi.bak -we "BEGIN { @ARGV = map glob, @ARGV }; s/{/'{ function(' . $i++ . ')'/ge" *.h *.c

Это почти достаточно, чтобы создать скрипт.

Если вам нужна рекурсия, вы должны использовать File :: Find . Обратите внимание, что этот код в значительной степени идентичен функциональности, как выше.

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

my $dir = "d:/testing perl";   # 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;
    print;
}

Не убаюкивайте себя в ложном смысле безопасности с помощью опции резервного копирования. Если вы дважды запускаете этот скрипт, эти резервные копии будут перезаписаны, поэтому имейте это в виду.

Не нужно ли сохранить фигуру?
добавлено автор Zaid, источник
Правильно, не заметил этого.
добавлено автор TLP, источник
@ МихайловСоголовский заповедник означает «держать», и теперь он исправлен. Большинство ответов perl здесь используют одно и то же основное регулярное выражение, поэтому самая большая проблема - поиск и редактирование файлов, я бы сказал.
добавлено автор TLP, источник
что означает сохранение?
добавлено автор MiSo, источник
$ perl -pi -e 's| (?<={) | q#function(# . ($i++) . q#)# |gex' *.c *.h
2
добавлено
Однако это не будет работать для окон.
добавлено автор TLP, источник

Это можно сделать в одной строке, как показано ниже:

perl -pi -e 's/({)/"{function(".++$a.")"/ge;' your_file

Я только что взял примерный файл ввода и протестировал его.

> cat temp
line-1 {    {    {   {
line-2 {    {   {
line-3 {    {
line-4 {

Теперь выполнение:

> perl -pi -e 's/({)/"{function(".++$a.")"/ge;' temp
> cat temp
line-1 {function(1)    {function(2)    {function(3)   {function(4)
line-2 {function(5)    {function(6)   {function(7)
line-3 {function(8)    {function(9)
line-4 {function(10)
1
добавлено
Обновлен мой ответ, удалив блок BEGIN и добавив замену на место.
добавлено автор Vijay, источник
За исключением того, что вам не нужно инициализировать $ a при использовании ++ , а OP использует окна, поэтому обработка аргументов и редактирование на месте (которые вы не используете использование) не будет работать.
добавлено автор TLP, источник

Используя awk '/ {/ {gsub (/ {/, "{function (" i ++ ")"), print; next} {print}' и ваш код в качестве входных данных:

$ awk '/{/{gsub(/{/,"{function("i++")");print;next}{print}' file
sub fileMode 
{function(0)
 my @files = <*[ch]>;   # get all files ending in .c or .h
 foreach $file (@files) # go through all the .c and .h flies in the directory  
{function(1)
    if (-f $file)  # check if it is a file or dir
    {function(2)
    my @lines;
# copy each line from the text file to the string @lines and add a function call after every     '{function(3)' '
    tie @lines, 'Tie::File', $file or die "Can't read file: $!\n";
    foreach ( @lines )
    {function(4)
        if (s/{function(5)/@newString/g)
        {function(6)
            $counter++;
            @newString = '{function(7)function('.$counter.')';
        }
        untie @lines; # free @lines
    }
    }
}   
}

Примечание. Номер функции не будет увеличен для встроенного вложенного {.

$ echo -e '{ { \n{\n-\n{' | awk '/{/{gsub(/{/,"{function("i++")");print;next}1'
{function(0) {function(0) 
{function(1)
-
{function(2)

Объяснение:

/{/                               # For any lines that contain {
gsub( /{/ , "{function("i++")" )  # replace { with function(i++)
print;next # print the line where the replacement happened and skip to the next
print                             # print all the lines 
1
добавлено
@MichaelSogolovsky Я добавил объяснение.
добавлено автор Chris Seymour, источник
Да просто передайте свой фактический файл.
добавлено автор Chris Seymour, источник
Спасибо за быстрый ответ. не могли бы вы объяснить мне, как это работает. используя системный вызов awk I ment.
добавлено автор MiSo, источник
Спасибо за быстрый ответ. не могли бы вы объяснить мне, как это работает? Эта строка заменяет '{' в одном файле? вместо «файла» мне нужно указать имя файла? или путь? И снова Спасибо!
добавлено автор MiSo, источник
Modern::Perl
Modern::Perl
362 участник(ов)

Пожалуйста, представьтесь при добавлении в группу. Это необходимо во избежание спам-ботов. Ваше первое сообщение не должно быть ссылкой или репостом. Мы всегда рады нестандартным вопросам.

use Perl or die;
use Perl or die;
164 участник(ов)

Группа о языке Perl и обо всём что с ним связано.

pro.perl
pro.perl
22 участник(ов)

Язык программирования Perl