14

特定のディレクトリにあるすべての .config ファイルを取得する必要があり、これらの各ファイルで特定の文字列を検索し、ファイルに基づいて別の文字列に置き換える必要があります。

たとえば、指定されたディレクトリに3つのファイルがある場合:

 for  my_foo.config - string to search "fooCommon >" replace with "~ /fooCommon[\/ >"
 for  my_bar.config - string to search "barCommon >" replace with "~ /barCommon[\/ >"
 for  my_file.config - string to search "someCommon >" replace with "~ /someCommon[\/ >"

Perl でこれを行う方法を教えてください。

以下は、シェルスクリプトで試したコードです。

OLD="\/fooCommon >"
NEW="~ \"\/fooCommon[^\/]*\" >"
DPATH="/myhome/aru/conf/host*.conf"
BPATH="/myhome/aru/conf/bakup"
TFILE="/myhome/aru/out.tmp.$$"
[ ! -d $BPATH ] && mkdir -p $BPATH || :
for f in $DPATH
do
  if [ -f $f -a -r $f ]; then
   /bin/cp -f $f $BPATH
   echo sed \"s\/$OLD\/$NEW\/g\"
   sed "s/$OLD/$NEW/g" "$f" > $TFILE && mv $TFILE "$f"
  else
   echo "Error: Cannot read $f"

fi
done
/bin/rm $TFILE
4

7 に答える 7

34

Unix ライクなプラットフォームを使用している場合は、コマンド ラインで Perl を使用して実行できます。スクリプトを書く必要はありません。

perl -i -p -e 's/old/new/g;' *.config

より安全を期すために、バックアップ オプションを指定してコマンドを使用することをお勧めします。

perl -i.bak  -p -e 's/old/new/g;' *.config
于 2013-11-15T08:39:50.877 に答える
11

ここでの Perl はファイルを変更するだけです...次のようにもっと簡単にできるのに、なぜ perl で全体を書く必要があるのか​​ わかりません。

find . -maxdepth 1 -type f -name '*.conf' | \
    xargs perl -i.bak -pe 's/localhost/example.com/;'
于 2013-11-15T08:37:31.743 に答える
3

本当に perl のみでこれを行う必要がある場合は、優れた簡単な回答が既に投稿されているためお勧めしません。

#!/usr/bin/perl

# take the directory to be processed from first command line argument
opendir($dh, $ARGV[0]);
# take only relevant files ie. "*.config"
@cfgs = grep { /\.config$/ } readdir($dh);
# loop through files
foreach(@cfgs) {
  # generate source string from the filename
  ($s) = ($_ =~ /.*_(\w+)\.config.*/);
  $s = "${s}Common";
  # generate replacement string from the filename
  $r = "~ /${s}[/ >";
  # move original file to a backup
  rename("${ARGV[0]}${_}", "${ARGV[0]}${_}.bak");
  # open backup file for reading
  open(I, "< ${ARGV[0]}${_}.bak");
  # open a new file, with original name for writing
  open(O, "> ${ARGV[0]}${_}");
  # go through the file, replacing strings
  while(<I>) { $_ =~ s/$s/$r/g; print O $_; }
  # close files
  close(I);
  close(O);
}

# end of file.

単純な検索またはシェル ワイルドカードを使用してこれを行う方がはるかに簡単であることに注意してください。しかし、これはとにかく perl でファイルを処理する方法についてのちょっとしたチュートリアルだと思ってください。

于 2013-11-15T08:42:39.070 に答える
1

コマンド ラインから実行することもできますが、もう少し有用な出力を提供する簡単に使用できるスクリプトが必要な場合もあります。そのことを念頭に置いて、この質問に出くわした人にとってわかりやすい出力を備えた perl ソリューションを次に示します。

#!/usr/bin/env perl5.8.3

# subst [-v] [-f] "re/string to find" "string to replace" -- list of files
#  optional -v flag shows each line with replacement, must be 1st arg to script
#  optional -f flag says to disable regexp functionality and make the strings match exactly
#  replacement string may include back references ($1, $2, etc) to items in "string to find" if they are surrounded by grouping parenthesis

use strict;
use warnings;
use List::Util;
use IO::File;
use Fcntl;
use Getopt::Long qw(GetOptions);

my $verbose = 0;
my $fixed   = 0;

GetOptions("v" => \$verbose,
           "f" => \$fixed);

my $find    = shift @ARGV;
my $replace = shift @ARGV;

die "Error: missing 1st arg, string to find\n"         if not defined $find;
die "Error: missing 2nd arg, string to replace with\n" if not defined $replace;
die "No files were specified\n"                        if @ARGV == 0;

# open a temp file for writing changes to
my $TEMP = IO::File->new_tmpfile;
if (not defined $TEMP)
{
    print STDERR "ERROR: failed to create temp file: $!\n";
    exit 1;
}

# Fix max file name width for printing
my $fwidth = List::Util::max map { length $_ } @ARGV;

# Process each file
my $unchanged = 0;
my $changed   = 0;
foreach my $file (@ARGV)
{
    if (open(my $FILE, '<', $file))
    {
        # Reset temp file
        seek $TEMP, 0, SEEK_SET or die "ERROR: seek in temp file failed: $!";
        truncate $TEMP, 0       or die "ERROR: truncate of temp file failed: $!";

        # go through the file, replacing strings
        my $changes = 0;
        while(defined(my $line = <$FILE>))
        {
            if ($line =~ m/$find/g)
            {
                print "-" . $line if $verbose;
                print "\n" if $verbose and $line !~ m/\n$/;

                if ($fixed)
                {
                    my $index = index($line, $find);
                    substr($line, $index, length($find)) = $replace;
                }
                else
                {
                    $line =~ s/$find/replacebackrefs($replace)/eg;
                }

                $changes++;
                print "+" . $line if $verbose;
                print "\n" if $verbose and $line !~ m/\n$/;
            }

            print $TEMP $line;
        }
        close $FILE;

        if ($changes == 0)
        {
            $unchanged++;
            unlink("/tmp/subst$$");
            next;
        }

        # Move new contents into old file
        $changed++;
        printf "%*s - %3d changes\n", -$fwidth, $file, $changes;

        seek $TEMP, 0, SEEK_SET or die "ERROR: rewind of temp file failed: $!";
        open $FILE, '>', $file or die "ERROR: failed to re-write $file: $!\n";
        while (<$TEMP>) { print $FILE $_ }
        close $FILE;

        print "\n" if $verbose;
    }
    else
    {
        print STDERR "Error opening $file: $!\n";
    }
}

close $TEMP;

print "\n";
print "$changed files changed, $unchanged files unchanged\n";

exit 0;

sub replacebackrefs
{
    # 1st/only argument is the text matched
    my $matchedtext = shift @_;

    my @backref;
    # @- is a dynamic variable that holds the offsets of submatches in
    # the currently active dynamic scope (i.e. within each regexp
    # match), corresponding to grouping parentheses. We use the count
    # of entrees in @- to determine how many matches there were and
    # store them into an array. Note that @- index [0] is not
    # interesting to us because it has a special meaning (see man
    # perlvar for @-)\, and that backrefs start with $1 not $0.
    # We cannot do the actual replacement within this loop.
    do
    {
        no strict 'refs'; # turn of warnings of dynamic variables
        foreach my $matchnum (1 .. $#-)
        {
            $backref[$matchnum] = ${$matchnum}; # i.e. $1 or $2 ...
        }
    } while(0);

    # now actually replace each back reference in the matched text
    # with the saved submatches.
    $matchedtext =~ s/\$(\d+)/$backref[$1]/g;

    # return a scalar string to actually use as the replacement text,
    # with all the backreferences in the matched text replaced with
    # their submatch text.
    return $matchedtext;
}
于 2014-12-22T23:19:43.297 に答える
0

あなたのスクリプトは良い試みです。

いくつかの冗長性が含まれています。

  • するのは無意味ですcp $f
  • $TFILE同様に役に立たない(sed出力をターゲットファイルに直接書き込むだけです)

次のように、ディレクトリ パスを使用せずに$NEWの値からターゲット ファイル名を作成できます。$f

bf=`basename "$f"`
于 2014-12-23T08:08:10.897 に答える
0

おそらく、以下が役立つでしょう:

use strict;
use warnings;

my %replacements =
  map { chomp; my @x = split /\|/; $x[0] => [ $x[1], $x[2] ] } <DATA>;

local $^I = '.bak';

for my $file (<*.config>) {
    push @ARGV, $file;

    while (<>) {
        s/\b\Q$replacements{$file}[0]/$replacements{$file}[1]/g;
        print;
    }
}

__DATA__
my_foo.config|fooCommon >|~ /fooCommon[/ >
my_bar.config|barCommon >|~ /barCommon[/ >
my_file.config|someCommon >|~ /someCommon[/ >

配列のハッシュ (HoA) は、 - で区切られた DATA 行をsplitting することによって構築され|ます。ここで、キーはファイル名であり、値は、2 つの要素がファイルの置換用である無名配列への参照です。このlocal $^I = '.bak'表記は、元のファイルのバックアップを作成します。

置換を調整する必要がある場合があります。たとえば、 in を使用\bした置換では単語境界が観察されs/\b\Q$replacements{$file}[0]/$replacements{$file}[1]/g;ます。これが必要な場合とそうでない場合があります。

元のファイルがバックアップされている場合でも、完全に実装する前に、最初に1つの「スクラッチ」ファイルでのみ試して、必要な結果が得られることを確認することをお勧めします.

于 2013-11-15T19:54:57.307 に答える