0

settings.py次のようなテスト ファイルが与えられます。

# Django settings for x project.
DEBUG = True
TEMPLATE_DEBUG = DEBUG
ADMINS = (
    # ('Your Name', 'your_email@example.com'),
)
MANAGERS = ADMINS
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': '',                      # Or path to database file if using sqlite3.
        'USER': '',                      # Not used with sqlite3.
        'PASSWORD': '',                  # Not used with sqlite3.
        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
    }
}
# Hosts/domain names that are valid for this site; required if DEBUG is False
# See https://docs.djangoproject.com/en/1.3/ref/settings/#allowed-hosts
ALLOWED_HOSTS = []

プログラム(シェルスクリプト)で行間の部分を置き換えたい:

DATABASES = {

と:

}

variable にテキストが含まれている場合k:

declare -r k='foo bar baz'

私はperl初心者ですが、これを作成しました:

perl -ne 'if(!$f && /DATABASES/){$f=1} if(!$f){print} if($f && /^}$/){$f=0}' < settings.py

sedこれは、私の通常の/awk小さなハックからの逸脱です:

# e.g.
sed '/DATABASES/,/^}$/ d' < settings.py

perlワンライナーを改善したい!

sedオールマイティでこんなにも美しいものをどうやって作ればいいのperl

絶対に最善の方法は次のとおりです。

  • 標準入力が通り過ぎるのを見て、それを標準出力に再現する
  • センチネルの「印刷停止」行を検出し、コピーを停止します
  • 2 番目のセンチネル行に遭遇したときに stdin->stdout のパススルーを再度有効にします

タスクの置換部分も省略しましたが、それについても助けが得られることを望んでいます。

4

3 に答える 3

2

なぜ perl を単純なテキスト操作に使用したいのか想像できません。それは awk が設計された目的であり、すべての優れた UNIX ツールと同様に、awk は 1 つのことをうまく実行します。

GNU awk の場合:

$ k="<<<< foo >>>>"
$ gawk -v k="$k" -v RS='\0' '{sub(/DATABASES = {.*\n}/,k)}1' file
# Django settings for x project.
DEBUG = True
TEMPLATE_DEBUG = DEBUG
ADMINS = (
    # ('Your Name', 'your_email@example.com'),
)
MANAGERS = ADMINS
<<<< foo >>>>
# Hosts/domain names that are valid for this site; required if DEBUG is False
# See https://docs.djangoproject.com/en/1.3/ref/settings/#allowed-hosts
ALLOWED_HOSTS = []

説明:

gawk
-v k="$k"     = set the awk variable k to the value of the shell variable k
-v RS='\0'    = set the Record Separator to the NULL string so gawk reads the whole file
'
{sub(/DATABASES = {.*\n}/,k)}     = replace the text between "DATABASES = {" and "}" at the start of a line inclusive with the contents of the awk variable k.
1     = set a true condition which invokes the default action of printing the current record (the whole file in this case)
' file

メモリの制約のために一度にファイル全体を読み取ることができない場合、または単にこのスタイルを好む場合、または GNU awk を持っていない場合は、スクリプトを次のように変更します (未テスト):

$ awk -v k="$k" '
    /DATABASES = {/ { skip=1 }
    skip && /^}/    { skip=0; $0=k }
    !skip
  ' file

うまくいけば、それが何をするかは明らかです。RS='\0' の設定を削除すると、スクリプトが gawk 固有ではなくなることに注意してください。

区切り行を保持する必要がある場合は、それも微調整です。

$ awk -v k="$k" '
    skip && /^}/    { skip=0; print k }
    !skip
    /DATABASES = {/ { skip=1 }
  ' file
# Django settings for x project.
DEBUG = True
TEMPLATE_DEBUG = DEBUG
ADMINS = (
    # ('Your Name', 'your_email@example.com'),
)
MANAGERS = ADMINS
DATABASES = {
<<<< foo >>>>
}
# Hosts/domain names that are valid for this site; required if DEBUG is False
# See https://docs.djangoproject.com/en/1.3/ref/settings/#allowed-hosts
ALLOWED_HOSTS = []
于 2013-04-02T14:27:38.657 に答える
1

awk スクリプトを Perl スクリプトに変換する方法を紹介したいと思いました。

まず、Ed Morton の awk バージョンを使用して、a2p.

$ a2p
/DATABASES = {/ { skip=1 }
skip && /^}/    { skip=0; $0=k }
!skip
^d

+^dを押すことを表すことに注意してください。Ctrld

#!/opt/perl-5.14.1/bin/perl
eval 'exec /opt/perl-5.14.1/bin/perl -S $0 ${1+"$@"}'
    if $running_under_some_shell;
            # this emulates #! processing on NIH machines.
            # (remove #! line above if indigestible)

eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_0-9]+=)(.*)/ && shift;
            # process any FOO=bar switches

while (<>) {
    chomp;  # strip record separator
    if (/DATABASES = {/) {
    $skip = 1;
    }
    if ($skip && /^}/) {
    $skip = 0;
    $_ = $k;
    }
    print $_ if !$skip;
}

ラインを捨てることができeval 'exec ...ます。あなたがそれを必要とすることはないと思います。

を処理するだけなのでk="$k"eval '$'.$1.'$2;' ...も同様に捨てることができます。前者を後者に設定$kする$ENV{k}か、前者に置き換えるだけです。(export kこれを機能させるには呼び出す必要があることに注意してください。単に呼び出すこともできますenv k="$k" perl test.pl

行がchompedprint $_ if !$skip;になるため、に置き換えるか、にprint $_, "\n" if !$skip;設定$\する必要があり"\n"ます。に電話せずに逃げることができると思いますchomp

また、見つけにくい間違いを防ぐために、最初に追加use strict;します。use warnings;

#!/usr/bin/env perl
use strict;
use warnings;

my $skip; # prevents printing when true
while (<>) {
  if (/DATABASES = {/) {
    $skip = 1;
  }
  if ($skip && /^}/) {
    $skip = 0;
    $_ = $ENV{k}."\n";
  }
  print $_ if !$skip;
}

sedここに「イズム」を混ぜることができると思います。( ...)

#!/usr/bin/env perl
use strict;
use warnings;

while (<>) {
  if( my $r = /DATABASES = {/ ... /^}/ ){

    if( $r == 1 ){ # first time it matches
      print $ENV{k}, "\n";
    }

    next; # don't print
  }

  print;
}

唯一のことは、OPがとの間 DATABASES = {のテキストを置き換えたかったと思う}. そのため、これら 2 行を印刷できるようにコードを追加する必要があります。

#!/usr/bin/env perl
use strict;
use warnings;

while (<>) {
  if( my $r = /DATABASES = {/ ... /^}/ ){

    if( $r == 1 ){
      # append the replacement to the first line
      $_ .= $ENV{k}."\n";

    }elsif( $r !~ /E/ ){ # rest of the matches, except the last one
      next;
    }
  }

  print;
}

ご存知のように、置換テキストを環境変数に配置する必要があるのはあまり好きではありません。__DATA__セクションに配置してみてはいかがでしょうか。

use strict;
use warnings;

my $replacement = do{ local $/; <DATA> }; # slurp
close DATA;

while (<>) {
  if( my $r = /DATABASES = {/ .. /^}/ ){
    if( $r == 1 ){
      $_ .= $replacement;
    }elsif( $r !~ /E/ ){
      next
    }
  }
  print;
}

__DATA__
<<< FOO >>>
于 2013-04-02T21:38:17.240 に答える