シェルスクリプトでsedコマンドを使用してファイルエントリを1行ずつ変更する方法は?

シェルスクリプトでsedコマンドを使用してファイルエントリを1行ずつ変更する方法は?

これは私のものですfile1

#$BQ
#{ボリューム@ホーム}
#データベースバー
#relationshiptcdeatid
#コピー1
#{バージョン0}
#opendb
#クリア
#.ルノ:= 72
#.infno:=1
#.tid.noel := 101
#.tid.info:=64
#.tid.setnr:=1225              <---(1225番号が変更されます)
#.typeidm:=1
#.ソーステーブル:= 2
#writedb     
#クリア
#.ルノ:= 72
#.infno:=205
#.tid.noel := 101
#.tid.info:=76
#.tid.setnr:=5625              <---(5625番号が変更されます)
#.typeidm:=1
#.ソーステーブル:= 2
#writedb
#$EOJ

file2正しい項目があります。

#.tid.info:=3345              <---(この番号を入力したいと思います。ファイル164の代わりに)
#.tid.setnr:=1254              <---(この番号を入力したいと思います。ファイル11225の代わりに)
#.tid.info:=5567              <---(この番号を入力したいと思います。ファイル176の代わりに)
#.tid.setnr:=9056              <---(この番号を入力したいと思います。ファイル15625の代わりに)

file1からのデータに基づいて変更したいと思いますfile2

頑張りました sed "s/tid.setnr := 1225/tid.setnr := 1254/g" file1 > modified_file1

しかし、これを手動で実行したくありません。スクリプトで新しい値と値を読み取って変更するようにtid.infoしたいとtid.setnr思いfile2ますfile1file2には、等が含まれています。file1tid.infofile2file1tid.setnr

答え1

GNU sed R コマンドを使用します。

sed -e 's/^#.tid.setnr :=.*//;tA;b;:A;R file2' -e 'd' file1

答え2

これを行う1つの方法はスクリプトをsed生成することです。sed

tmpfile=/tmp/Nainita.$$
exec 3< file2
grep -n "tid\.setnr" file1 | while IFS=: read n junk
do
        IFS= read -r line <&3  ||  break
        printf "%sc%s\n%s\n" "$n" '\' "$line"
done > "$tmpfile"
exec 3<&-
sed -f "$tmpfile" file1 > modified_file1
rm -f "$tmpfile"
  • exec 3< file2file2ファイル記述子を読むために開きます。 3.
  • 行番号で次の行を探しますgrep -n。これはループに供給されます。file1tid.setnrwhile
  • while IFS=: read n junk

    • while … read …一度に1行ずつ読み取ることを意味し、読み取る情報がある限り繰り返します(つまり、データの終わりに達すると停止)。
    • IFS=: read n junk: 最初の行(つまり元の行番号)の前のgrep -nすべての項目を読み取り、残りのn行(前のデータ行)tid.setnrを読み取ることを意味しますjunk。これは気にしないので無視します。
  • IFS= read -r line <&3ファイル記述子3()から1行を読みfile2、シェルがそれを破損させないように指示する方法をすべて実行し、名前付き変数に入れますline
  • … || break上記の操作がread失敗した場合(おそらくファイルの終わりのため)、ループを中止してください。
  • printf書いたsed hangeコマンドは行番号を指し、これはその行を変数の内容nで置き換える必要があることを示します。line
  • done > "$tmpfile"ループの終わりを示しwhile、ループ全体の標準出力があることを指定します$tmpfile。結果は次のとおりです。

    13c\
    #.tid.setnr:=1254
    22時\
    #.tid.setnr:=9056
    • while両方の入力のいずれかからEOFを取得すると、ループは終了します。したがって、行よりも多くの行がある場合、file1追加の行は変更されていません(つまり、対応するコマンドは生成されません)。同様に、より多くの行がある場合、追加の行は無視されます。残念ながら、この機能を追加することは難しくありませんが、この違いは報告されません。tid.setnrfile2cfile2tid.setnrfile1
  • exec 3<&-ファイル記述子を閉じます 3.

  • sed -f "$tmpfile" file1 > modified_file1sedで実行しfile1、からコマンドを読み取り、$tmpfile出力を書き込みますmodified_file1

必要に応じて動作する必要があります。もちろん、必要に応じてファイル名を変更できます。 「現状のまま」一度実行し、ファイルを見てmodified_file1(または出力をリダイレクトしないようにコマンドを変更してsed画面に書き込む)、出力が必要なものであることを確認する必要があります。その後、必要に応じてその場所で編集sedするようにコマンドを変更できます。sed -ifile1

sedコマンドで「行番号が多すぎます」などのエラーが発生した場合は、$tmpfileスクリプトファイル()をより小さなファイルに分割してみてください。各コマンドは2行なので、100個未満のコマンドサイズから始めることをお勧めします。たとえば、90のコマンドまたは180の行です。テキストエディタ1使用してこれを手動で実行できますが、この作業用に特別に作成されたツールがあります。直感的にsplit.commandと呼ばれます。

split --lines=180 "$tmpfile"

xaaスクリプトは現在のディレクトリで、、、、xab...というxacファイルに分割されます。最初N−1の長さは180行です。最後の項目は、全体を構成するために必要なすべての項目です(≤180)。たとえば、500個のインスタンスがある場合、スクリプトの長tid.setnrさは1000行になり、6個のxファイル( xaa、、、、)はそれぞれ180行で構成され、100行になります。今やるxabxacxadxaexaf

sed -f xaa file1 > modified_file1aa

それでも「行が多すぎます」というメッセージが表示されたら、戻って行数を減らしてもう一度やり直してください--lines。エラーが報告されない場合は、modified_file1aa最初の90行がtid.setnr変更されていることを確認してください。よさそうだと思います。

sed -f xab modified_file1aa > modified_file1ab
sed -f xac modified_file1ab > modified_file1ac
sed -f xad modified_file1ac > modified_file1ad
sed -f xae modified_file1ad > modified_file1ae
sed -f xaf modified_file1ae > modified_file1af

modified_file1af今最後ですmodified_file1

実験したい場合は、次のことができます。

  • --lines最大値が何であるかが見つかるまで、より大きな数字を試してください。
  • 努力する

    sed -f xaa -f xab -f xac -f xad -f xae -f xaf file1 > modified_file1_test
    

    しかし、これはうまくいかないかもしれません。

  • 上記の実験を行ったら、結果を教えてください。

これを一度だけ行うと完了です。ただし、これを繰り返し実行する必要がある場合は、この回答の上部にあるコードブロックの最後の2行を次に変更してください。

分割 --lines=180 "$tmpfile"            <---(もちろん自分に合った数字を使用してください)
cp file1 変更されたファイル 1
fの場合x *
する
        sed -i -f "$f" 変更された_file1
完璧
rm -f "$tmpfile" x*

前述のように、この-iオプションはsed指定されたファイルをその場所で編集するように指示します(つまり、変更を入力ファイルに書き戻します)。またはより簡単に、

split --lines=180 "$tmpfile"
for f in x*
do
        sed -i -f "$f" file1
done
rm -f "$tmpfile" x*

原稿をそのまま維持する必要がない場合file1

使用法の概要は次のとおりですsplit

分けられる[オプション]… … [入力する[プレフィックス]]

デフォルトの動作はPREFIXaa、等という名前のファイルを生成することですPREFIXabPREFIXacつまり、デフォルトPREFIXはいx。名前で始まる別のファイルがある場合は、唯一のファイル(または)で定義し、上記のx内容を次のように変更する必要があります。prefixprefix=Nainita.$$.prefix=/tmp/Nainita.$$.

split --lines=180 "$tmpfile" "$prefix"
for f in "$prefix"*
do
        sed -i -f "$f" file1
done
rm -f "$tmpfile" "$prefix"*

______
1罰が好きなら、できるそうしてくださいsed- しかし、なぜ不気味なのですか?

答え3

{   sed '/^#\.tid\.setnr/!d;=;g;G' |
    paste  -d'c\\\n' - - - ./addfile
}   <./mainfile | sed -f - ./mainfile

関連行番号の前にのみ追加されます。./メインファイル各行に。 /ファイルを追加コマンド文字列c\<改行>編集のために結果をsed別のスクリプトにスクリプトに渡す前に./メインファイル。サンプルデータに基づいて生成されたスクリプトは次のとおりです。

13c\
#.tid.setnr := 1254              <--- (I want to put this number in file1 in place of 1225)
22c\
#.tid.setnr := 9056              <--- (I want to put this number in file1 in place of 5625)

...sedぶら下がっているc線を表します。13そして22各コマンドに一致する追加行を出力します。

答え4

パールを見つけました。このスクリプトは、トリックを実行しているように見えるおおよそのスクリプトです。

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

# subroutine to load a test file into an array
sub load{
   my $file = shift;
   open my $in, "<", $file or die "unable to open $file : $!";
   my @data = <$in>;
   chomp @data;
   foreach (@data) { s/\cM//g;}
   return @data;
}


my $file1 = shift || die "usage: $0 [file1] [file2]\n";
my @file1_data = &load($file1);
my $file2 = shift || die "usage: $0 [file1] [file2]\n";
my @file2_data = &load($file2);

my $i = 0;
foreach( @file1_data )
{
    if( $i < @file2_data ) 
    {
        my @s = split / /, $file2_data[$i];
        if( @s )
        {
            if( /^$s[0]/ )
            {
            $_ = $file2_data[$i++];
            }
        }
    }
    print "$_\n";

関連情報