web-dev-qa-db-ja.com

パターン(マーカー)の前にファイルのコンテンツを別のファイルに挿入する方法は?

File1の内容:

line1-file1      "1" 
line2-file1      "2"
line3-file1      "3" 
line4-file1      "4" 

File2の内容:

line1-file2     "25"  
line2-file2     "24"  
Pointer-file2   "23"  
line4-file2     "22" 
line5-file2     "21"

Perl/Shellスクリプトの実行後、File2の内容は次のようになります。

line1-file2     "25"  
line2-file2     "24" 
line1-file1      "1" 
line2-file1      "2"
line3-file1      "3" 
line4-file1      "4" 
Pointer-file2   "23" 
line4-file2     "22" 
line5-file2     "21"

つまり、「ポインタ」を含む行の前のFile1File2の内容を貼り付けます。

39
user1228191

sedにはそのための関数があり、インラインで変更を行うことができます。

sed -i -e '/Pointer/r file1' file2

しかし、これはあなたのPointer行をfile1の上に置きます。下に置くには、ライン出力を遅延させます。

sed -n -i -e '/Pointer/r file1' -e 1x -e '2,${x;p}' -e '${x;p}' file2 


GNU sed

sed '/Pointer/e cat file1' file2

manual に従ってe [command]

Rコマンドとは異なり、コマンドの出力はすぐに出力されることに注意してください。代わりに、rコマンドは現在のサイクルの終わりまで出力を遅延させます。

37
jfg956

sedまたはawk...を使用しない場合.

まず、あなたのパターンであるあなたの行を見つけてください:

line=$(grep -n 'Pointer' file2 | cut -d ":" -f 1)

次に、3つのコマンドを使用して、必要な結果を出力します。

{ head -n $(($line-1)) file2; cat file1; tail -n +$line file2; } > new_file

これには、ファイルに3回アクセスするという欠点がありますfile2ですが、sed of awkソリューションよりも明確な場合があります。

18
jfg956

awkはこれをかなり簡単にします。
ファイルの前の行を挿入します:

awk '/Pointer/{while(getline line<"innerfile"){print line}} //' outerfile >tmp
mv tmp outerfile

内部ファイルをPointer行の後に印刷するには、パターンの順序を入れ替えるだけで(デフォルトのアクションを取得するにはセミコロンを追加する必要があります)、line変数を削除できます。

awk '//; /Pointer/{while(getline<"innerfile"){print}}' outerfile >tmp
mv tmp outerfile

そして、誰もPerlをまだ使っていないからといって、

# insert file before line
Perl -e 'while(<>){if($_=~/Pointer/){system("cat innerfile")};print}' outerfile

# after line
Perl -e 'while(<>){print;if($_=~/Pointer/){system("cat innerfile")}}' outerfile
11
Kevin

edの簡単な仕事:

ed -s file1 <<IN
/Pointer/-r file2
,p
q
IN

-r file1は、指定されたファイルをアドレス指定された行の後(この場合はPointerに一致する最初の行の前の行)に読み取ります。したがって、これは、Pointerが複数行で発生した場合でも、file2のコンテンツを1回だけ挿入します。一致する各行の前に挿入する場合は、globalフラグを追加します。

ed -s file1 <<IN
g/Pointer/-r file2
,p
q
IN

ファイルをインプレースで編集する場合は、,pwに置き換えます。


受け入れられたsed回答はほとんどの場合に機能しますが、マーカーが最後の行にある場合、コマンドは期待どおりに機能しません。マーカーの後にFile1のコンテンツが挿入されます。
私は最初に試しました:

sed '/Pointer/{r file1
N}' file2

これもうまく機能します(rはサイクルの最後に魔法をかけるので)しかし、マーカーが最後の行にある場合は同じ問題があります(最後の行の後にNext行がない場合) )。これを回避するには、入力に改行を追加します。

sed '/Pointer/{              # like the first one, but this time even if the
r file1                      # marker is on the last line in File2 it
N                            # will be on the second to last line in
}                            # the combined input so N will always work;
${                           # on the last line of input: if the line is
/^$/!{                       # not empty, it means the marker was on the last
s/\n$//                      # line in File2 so the final empty line in the
}                            # input was pulled i\n: remove the latter;
//d                          # if the line is empty, delete it
}' file2 <(printf %s\\n)

これにより、一致する各行の前にfile2コンテンツが挿入されます。最初の一致する行の前にのみ挿入するには、loopを使用して、ファイルの終わりに到達するまでnext行をプルします。

sed '/Pointer/{
r file2
N
:l
$!n
$!bl
}
${
/^$/!{
s/\n$//
}
//d
}' file1 <(printf %s\\n)

これらのsedソリューションを使用すると、インプレースで編集できなくなります(ただし、別のファイルにリダイレクトできます)。

7
don_crissti

ループを使用してfile2の行を読み取ります。 Pointerで始まる行を見つけたら、file1を出力します。これを以下に示します。

#!/bin/bash
while IFS= read -r line
do
    if [[ "$line" =~ ^Pointer.*$ ]]
    then
        cat file1
    fi
    echo "$line"
done < file2
6
dogbane

sedを使用してこれを行うにはいくつかの方法があります。 1つの方法は、承認された回答で推奨されているように、読み取りを遅らせることです。次のように書くこともできます:

sed -e '$!N;P;/\nPointer/r file1' -e D file2

...ホールドバッファーを使用して他の場所に後読みを実装する代わりに、少し明示的な先読みを使用します。 Ndoesがラインサイクルをインクリメントするため、@ don_crisstiが注記する最後の行で同じ問題が発生します。 readコマンドは行番号で適用されます。

あなたはそれを回避することができます:

echo | sed -e '$d;N;P;/\nPointer/r file1' -e D file2 -

すべてのsedsが-を標準入力を意味すると解釈するわけではありませんが、多くの場合はそうです。 POSIXによるとsedは、実装者が-を標準にしたい場合、-を標準入力にサポートする必要がありますで???)

別の方法は、追加されたコンテンツを順番に処理することです。 schedulesreadと同じ方法で出力する別のコマンドがあり、sedがそれを適用してreadは、スクリプト化された順序で表示されます。ただし、少し複雑です。sedからappendを使用して、スクリプト内の別のPointerの出力にsedを一致させます。

sed '   /Pointer/!d                  #only operate on first match
        s/[]^$&\./*[]/\\&/g;H        #escape all metachars, Hold
        s|.*|/&/!p;//!d|p;g          #print commands, exchange
        s|.|r file1&a\\&|;q' file2|  #more commands, quit
        sed -nf - file2              #same input file

つまり、基本的に、最初のsedは2番目のsedをスクリプトに書き込み、2番目のsedは標準入力で読み取る(多分... )そして順番に適用されます。最初のsedは、見つかったPointerの最初の一致でのみ機能し、その後quits入力で機能します。その仕事は...

  1. s/[]^$&\./*[]/\\&/g;H
    • 2番目のsedは、文字を正しく読み取るために文字どおりに読み取るすべてのビットを解釈する必要があるため、すべてのパターン文字が安全にバックスラッシュエスケープされていることを確認してください。それが終わったら、コピーをHoldスペースに置きます。
  2. s|.*|/&/!p;//!d|p; x
    • 2番目のsedからprintにすべての入力行!を伝えますが、/&/はパターンセーフです。 deleteにすべて同じ。 print 2番目のコマンドでsedを実行し、次にe xchange holdとパターンバッファーを保存したコピーで機能するようにします。
  3. s|.|r file1&a\\&|p;q
    • ここで使用する唯一の文字は\newlineです。これは、前の行をsedeldするとHが前に付加されるためです。したがって、コマンドr file1を挿入し、その後に\newlineを続け、次にコマンドa\\ for appendの後に\newlineを続けます。残りのHeld行はすべて、最後の\newlineに従います。

最初のスクリプトは次のようになります。

/Pointer-file2   "23"/!p;//!d
r file1
a\
Pointer-file2   "23"

基本的に、2番目のsedはすべての行を出力しますが、最初のsedappendに設定します。その特定の行のtwo標準出力への遅延書き込みはscheduled- 1つ目はfile1readで、2つ目はその後に必要な行のコピーです。この場合、最初のsedの修正は不要です(参照?バックスラッシュなし)。ただし、パターンマッチが入力として再利用されるときはいつでも、ここで私が行う方法.

とにかく、...いくつかの方法があります。

4
mikeserv

[ファイルの内容を別のファイルのBEFOREパターンに挿入]

sed -i '/PATTERN/r file1' -e //N file2

【パターン後】

sed -i '/PATTERN/r file1' file2
3
Jim.Y

私の好みの方法:templating

sed 's/CHANGEME/$x/g' origfile | x="$(<file2insert)" envsubst '$x' > newfile

[〜#〜] changeme [〜#〜]origfileで発生するすべての内容をfile2insertの内容に置き換えます。最後のgをsedから削除して、最初に出現した[〜#〜] changeme [〜#〜]のみを置き換えます。

2
nrc

これはAWKを使用するとかなり簡単です。

パターン= "ポインタ"の前にFile1からFile2へ

最初にFile1の内容を変数にロードします

f1="$(<File1)"

次に挿入を行います

awk -vf1="$f1" '/Pointer/{print f1;print;next}1' file2

(または、「ポインタ」の後にFile1を挿入する場合)

awk -vf1="$f1" '/Pointer/{print;print f1;next}1' file2
2
Amos Folarin
 awk '/Pointer/{system("cat File1")}1' File2
0
dedowsdi