web-dev-qa-db-ja.com

ファイル内の各リストに何かを追加します

次のようなファイルlists.txtがあります。

// stuff at beginning of file

var list1 = new Array();
i = 0;
list1[i++] = 'a';
list1[i++] = 'b';
...
list1[i++] = 'z';

var list2 = new Array();
i = 0;
list2[i++] = 'a';
list2[i++] = 'b';
...
list2[i++] = 'z';

// other stuff at end of file

これらのリストのそれぞれに追加する必要があり(2つ以上あります)、次のようになります。

var list1 = new Array();
i = 0;
list1[i++] = 'a';
list1[i++] = 'b';
...
list1[i++] = 'z';
list1[i++] = 'something new';

var list2 = new Array();
i = 0;
list2[i++] = 'a';
list2[i++] = 'b';
...
list2[i++] = 'z';
list2[i++] = 'another thing';

// other stuff at end of file

私はしばらくの間、これについて頭を悩ませてきました。各リストの最後のオカレンスを取得する方法を知っています。

list1_last=$(grep "list1\[i++\]" lists.txt | tail -1)
list2_last=$(grep "list2\[i++\]" lists.txt | tail -1)

最初のリストの開始から2番目のリストの開始までのすべてを取得する方法を知っています(包括的):

list1=$(sed -n '/var list1/,/var list2/p' lists.txt)

私は、list2の最初の行がなくてもlist1を取得できることを知っています このPerlワンライナー または このクレイジーなsedスクリプト

しかし、私はすべてのピースをまとめるのに苦労しています。どうすればよいですか?

編集

追加したい追加の値は、別のファイル、additional-values.txtにあります。これには、たとえば次のものが含まれています。

list1[i++] = 'something new';
list2[i++] = 'another thing';

私は2つのファイルをマージしようとしていると言えるでしょう。

編集2

実際のファイル は次のようになります:

// comment
// comment
// ...
var foo = "bar";

// comment
// comment
// ...
var i= 0;

// comment
// comment
// ...
var GoodDomains = new Array();
i=0;
GoodDomains[i++] = "anything.com";  // comment
GoodDomains[i++] = "something.com"; // comment
...
GoodDomains[i++] = "lastthing.com"; // comment
// THIS IS WHERE I WANT TO INSERT SOMETHING

// comment
// comment
// ...
var BadDomains = new Array();
i=0;
BadDomains[i++] = "anything.com";  // comment
BadDomains[i++] = "something.com"; // comment
...
BadDomains[i++] = "lastthing.com"; // comment
// THIS IS WHERE I WANT TO INSERT SOMETHING

// more lists, including GoodHosts, GoodURLs, etc.

// comment
// comment
// ...
for (i in GoodDomains) {
    ...
}

// loop through BadDomains, GoodHosts, GoodURLs, etc.

// comment
// comment
// ...
function IsNumIpAddr(Host) {
    ...
}

もともと簡略版を投稿したのは

  1. 実際のファイルが常にこの形式に従うかどうかはわかりません(上部のコメント、変数宣言、その他のコメント、リスト定義、関数など)。
  2. 問題の一般的な解決策を見つけたい(ファイルの途中でリストに何かを追加する)

これが誤解を招く場合は申し訳ありません。

4
Big McLargeHuge

sedの範囲で試しているので、これを行うための1つの可能な方法があります。 _additional-values.txt_の行は同じパターンに従います。

_KEY[i++] = 'VALUE'; //etc
_

そして私が知る限り、各行は常にで区切られる範囲に挿入する必要があります

_var KEY = new Array();
_

および空の行

したがって、_additional-values.txt_を処理し、それをsedスクリプトに変換できます。

_/^var KEY = new Array();/,/^$/{
/^$/ i\
KEY[i++] = 'VALUE'; // etc
}
_

つまり、/^var KEY = new Array();/,/^$/の範囲で、空の行の前に行_KEY[i++] = 'VALUE'; // etc_を挿入します。次に、スクリプトを使用して_lists.txt_を処理します。

_sed 's/\\/&&/g' additional-values.txt | \
sed 's|^\([^[]*\).*|/^var \1 = new Array();/,/^$/{\
/^$/ i\\\
&\
}|' | sed -f - lists.txt
_

最初のsedはバックスラッシュをエスケープし、2番目のsedは_additional-values.txt_を処理して、3番目のsedが使用するスクリプトに変換します(_-f_経由) )_lists.txt_を処理します。
例えば。サンプル_additional-values.txt_コンテンツ:

_GoodDomains[i++] = '^stuff/here/'; \
BadDomains[i++] = '%XYZ+=?\\<>';
GoodNetworks[i++] = '|*{};:\'; // Malware\\
BadDomains[i++] = '\$.|&$@"#"!||';
_

結果として:

_sed 's/\\/&&/g' additional-values.txt | \
sed 's|^\([^[]*\).*|/^var \1 = new Array();/,/^$/{\
/^$/ i\\\
&\
}|'
_

です

_/^var GoodDomains = new Array();/,/^$/{
/^$/ i\
GoodDomains[i++] = '^stuff/here/'; \\
}
/^var BadDomains = new Array();/,/^$/{
/^$/ i\
BadDomains[i++] = '%XYZ+=?\\\\<>';
}
/^var GoodNetworks = new Array();/,/^$/{
/^$/ i\
GoodNetworks[i++] = '|*{};:\\'; // Malware\\\\
}
/^var BadDomains = new Array();/,/^$/{
/^$/ i\
BadDomains[i++] = '\\$.|&$@"#"!||'; 
}
_

次に、これは_sed -f - lists.txt_に渡されます。サンプル_lists.txt_:

_// Counter Variable to initalize the arrays.
var i= 0;

var GoodDomains = new Array();
i=0;
GoodDomains[i++] = 'aba.com'; // Phish - 2010-02-05

var GoodNetworks = new Array();
i=0;
GoodNetworks[i++] = '10.0.0.0, 255.0.0.0';  // NRIP
// GoodNetworks[i++] = "63.140.35.160"; // DNSWCD 2o7

var BadDomains = new Array();
i=0;
BadDomains[i++] = '.0catch.com'; // AdServer - 2009-06-16

//var BadDomains = new Array();
_

ランニング:

_sed 's/\\/&&/g' additional-values.txt | \
sed 's|^\([^[]*\).*|/^var \1 = new Array();/,/^$/{\
/^$/ i\\\
&\
}|' | sed -f - lists.txt
_

出力:

_// Counter Variable to initalize the arrays.
var i= 0;

var GoodDomains = new Array();
i=0;
GoodDomains[i++] = 'aba.com'; // Phish - 2010-02-05
GoodDomains[i++] = '^stuff/here/'; \

var GoodNetworks = new Array();
i=0;
GoodNetworks[i++] = '10.0.0.0, 255.0.0.0';  // NRIP
// GoodNetworks[i++] = "63.140.35.160"; // DNSWCD 2o7
GoodNetworks[i++] = '|*{};:\'; // Malware\\

var BadDomains = new Array();
i=0;
BadDomains[i++] = '.0catch.com'; // AdServer - 2009-06-16
BadDomains[i++] = '%XYZ+=?\\<>';
BadDomains[i++] = '\$.|&$@"#"!||'; 

//var BadDomains = new Array();
_

_gnu sed_とプロセス置換を好む場合:

_sed -E 's|^([^[]*).*|/^var \1 = new Array();/,/^$/{/^$/ i\\\n&\
}|' <(sed 's/\\/&&/g' additional-values.txt) | sed -f - lists.txt
_
3
don_crissti

ファイルを逆にすると、次の行を追加できます最初何かが表示されたとき:

tac lists.txt |
awk -v l1="list1" -v val1="something new" \
    -v l2="list2" -v val2="another thing" '
          index($0, l1"[i++]") && !found1 {
              printf "%s[i++] = \"%s\";\n", l1, val1
              found1 = 1
          }
          index($0, l2"[i++]") && !found2 { 
              printf "%s[i++] = \"%s\";\n", l2, val2
              found2 = 1
          }
          {print}
' |
tac > lists.txt.new

少し乾燥していませんが、問題ありません。

「additional-values.txt」があるのを見逃しました。この方法の方がはるかに優れています。

tac lists.txt | 
awk '
    NR == FNR {additional[$1] = $0; next}
    $1 in additional && !found[$1] {print additional[$1]; found[$1] = 1}
    {print}
' additional-values.txt - | 
tac > newfile
4
glenn jackman

入力ファイルのリストが空白行で区切られている場合は、レコード区切り文字(「行」を定義するもの)を連続する改行に設定できるツールを使用できます。たとえば、Perlの場合(置換がadditionsというファイルにあると仮定):

Perl -ne 'BEGIN{## Open the additions file
                open($fh,"additions"); 
                while(<$fh>){ 
                  ## Get the name of the current list
                  /list./; 
                  ## save this replacement in the %f hash
                  $f{$&}=$_;
                }
                ## Set the record separator to consecutive newlines.
                $/="\n\n";
               }
          ## Now that the BEGIN{} block is finished, process the
          ## input file.

         ## Does this line match "list."? 
         if(/list./){
            chomp; ## remove trailing newlines. 
            ## Add the addition to this "line"
            $_.= "\n$f{$&}\n\n"; 
          } 
         ## print each input line
         print ' file 

上記は次のように要約できます。

Perl -ne 'BEGIN{open($fh,"additions"); while(<$fh>){/list./;$f{$&}=$_;}$/="\n\n";}
         if(/list./){chomp;$_.= "\n$f{$&}\n\n"; }; print ' file 
2
terdon

あなたのリストがこのような新しい行で区切られているとすると

var list1 = new Array();
i = 0;
list1[i++] = 'a';
list1[i++] = 'b';
list1[i++] = 'z';

var list2 = new Array();
i = 0;
list2[i++] = 'a';
list2[i++] = 'b';
list2[i++] = 'z';\n

そして、additional-lists.txtが次のようになっている場合:

list1[i++] = 'something new';
list2[i++] = 'another thing';

次に、このbash/sedスクリプトは目的の出力を生成します。

#! /bin/bash
a="lists.txt"
b="additional-values.txt"
while read line; do
    list=$(expr match "$line" '\(.*\[\)')   
    list=${list::-1}
    sed -i "/$list\[i++\]/{:loop; n; /^$/{s/^$/$line\n/; b}; b loop;}" $a
done < $b

これを行うには、additional-values.txtの各行を読み取り、その行の部分文字列を[(additional-lists.txtの形式は[i ++] ...であると想定しています)まで取得します。例: "list1 ["の場合、最後の文字を削除してリスト名を取得します。次に、リスト名に一致するsedスクリプトを開始し(bash変数を使用するために二重引用符を使用していることに注意してください)、空白行に達すると終了するループを開始します。最後に、空白行を追加の値(および改行)からの行に置き換えます。 -iオプションは、その場で編集することを意味します。

出力:

 $ cat lists.txt
 var list1 = new Array();
 i = 0;
 list1[i++] = 'a';
 list1[i++] = 'b';
 list1[i++] = 'z';
 list1[i++] = 'something new';

 var list2 = new Array();
 i = 0;
 list2[i++] = 'a';
 list2[i++] = 'b';
 list2[i++] = 'z';
 list2[i++] = 'another thing';
1
bkmoney

Awkのレコード区切り文字RSを次のように設定して使用します:リストの最後の行と次の空白行

仕組み

最初は、RS\n(デフォルト)— 1番目の引数で指定された入力ファイルの場合:additional-values.txt
最初のファイルを読み込んだ直後に、awkRSの値を2番目の引数の値に変更します。
2番目のファイルlists.txt、3番目に名前が付けられ、argには2番目のargによって定義されたRSがあります

1:追加の値の配列を作成する
line 2:分割の最初のフィールドは現在のリストのキーです— RT経由(RSによるテキスト)
line :print record + record-separator(less one \n)+追加の値

 awk 'RS == "\n" { addval[$1] = addval[$1] $0 "\n"; next }
   { split(RT,crskey) 
     print $0 gensub(/\n/,"","",RT) addval[ crskey[1] ] 
   }' additional-values.txt \
      RS='[^[\n]+[[]i[+][+][]] = [^;\n]+;\n\n' \
      lists.txt
1
Peter.O

私はついにうまくいくものを思いついた:

# print from beginning of file to "var list1" (exclusive)                                                                                 
sed "/var list1/,\$d" lists.txt > merged.txt

# print from "var list1" to last member of array
lastlist1=$(grep -n "list1\[i++\]" lists.txt | tail -1 | cut -f1 -d:)
sed -n "/var list1/,$(echo $lastlist1)p" lists.txt >> merged.txt
grep "^list1" additional-values.txt >> merged.txt

# print from "var list2" to last member of array
lastlist2=$(grep -n "list2\[i++\]" lists.txt | tail -1 | cut -f1 -d:)
sed -n "/var list2/,$(echo $lastlist2)p" lists.txt >> merged.txt
grep "^list2" additional-values.txt >> merged.txt

# do this for list3, list4,... listn

# print from last member of listn (exclusive) to end of file
sed "1,$(echo $lastlistn)d" lists.txt >> merged.txt

これはかなり退屈で、おそらく改善できるでしょうが、少なくとも私はそれを理解しています。

1
Big McLargeHuge