web-dev-qa-db-ja.com

Bash:パターンの後に1つのファイルのコンテンツを別のファイルに挿入する

私はbashスクリプトを書き込もうとしています。これは次のことを行います。

  1. 最初のファイルからコンテンツを読み取ります(最初の引数として)
  2. 2番目のファイルからコンテンツを読み取ります(2番目の引数として)
  3. 指定されたパターンで2番目のファイルの行を検索します(3番目の引数として)
  4. パターンの行の後に、最初のファイルから2番目のファイルにテキストを挿入します。
  5. 画面に最終ファイルを印刷します。

例えば:

first_file.txt:

111111
1111
11
1

second_file.txt:

122221
2222
22
2

パターン:

2222

出力:

122221
111111
1111
11
1
2222
111111
1111
11
1
22
2

BASHでこの機能を実現するには何を使用すればよいですか?

コードを書きましたが、正しく機能しません(なぜですか?):

    #!/bin/bash

    first_filename="$1"
    second_filename="$2"
    pattern="$3"

    while read -r line
    do
    if [[ $line=˜$pattern ]]; then
            while read -r line2
            do
                    echo $line2
            done < $second_filename
    fi
    echo $line
    done < $first_filename
12
user2431907

=~演算子の前後にスペースが必要です。比較:

[[ foo=~bar ]]
[[ foo =~ bar ]]

これは、最初の式が基本的に「この文字列は空ですか?」と評価されるためです。

また、OPコードは チルダではなく 小さなチルダ を使用します。

それでも、内側のループを簡単に取り除くことができます。 while read -r line2ビット全体をcat -- "$second_filename"に置き換えるだけです。

最後のecho $lineが正しいのは、ファイルが改行文字で終わっていない場合のみです(* nixツールの標準)。代わりに、while read -r line || [[ $line ~= '' ]]を使用する必要があります。これは、最後に改行がある場合とない場合で機能します。

また、 se MoreQuotes™

1
l0b0

sedはループなしでそれを行うことができます。そのrコマンドを使用します。

sed -e '/pattern/rFILE1' FILE2

テストセッション:

$ cd -- "$(mktemp -d)" 
$ printf '%s\n' 'nuts' 'bolts' > first_file.txt
$ printf '%s\n' 'foo' 'bar' 'baz' > second_file.txt
$ sed -e '/bar/r./first_file.txt' second_file.txt
foo
bar
nuts
bolts
baz
32
choroba

awkの使用も同様に機能します。

###マーカー###行の前に挿入するには:

// for each <line> of second_file.txt :
//   if <line> matches regexp ###marker###, outputs first_file.txt.
//   **without any condition :** print <line>
awk '/###marker###/ { system ( "cat first_file.txt" ) } \
     { print; } \' second_file.txt

###マーカー###行の後に挿入するには:

// for each <line> of second_file.txt :
//   **without any condition :** print <line>
//   if <line> matches regexp ###marker###, outputs first_file.txt.
awk '{ print; } \
     /###marker###/ { system ( "cat first_file.txt" ) } \' second_file.txt

### marker ###行を置き換えるには:

// for each <line> of second_file.txt :
//   if <line> matches regexp ###marker###, outputs first_file.txt.
//   **else**, print <line>
awk '/###marker###/ { system ( "cat first_file.txt" ) } \
     !/###marker###/ { print; }' second_file.txt

インプレース置換を行う場合は、一時ファイルを使用して、awkがファイル全体を読み取る前にパイプが開始されないようにします。追加 :

> second_file.txt.new
mv second_file.txt{.new,}
// (like "mv second_file.txt.new second_file.txt", but shorter to type !)

行の内側を置き換えたい場合(パターンだけを置き換え、行の残りを維持する)、同様の解決策がsedで達成できるはずです。 awkの代わりに。

私はこのようにsedを使用し、それは魅力として機能しました

sed -i -e '/ pattern/r filetoinsert' filetobeinserted

指定されたパターンの行の後に「filetoinsert」を「filetobeinserted」に挿入します。

一意のパターンを選択するように注意してください。重複するパターンでどのように機能するかはわかりません。最初のパターンだけで機能すると思います。

2
Julio Leiva

これは機能するはずです:

Perl -lne 'BEGIN{open(A,"first_file.txt");@f=<A>;}print;if(/2222/){print @f}' second_file.txt

テスト済み:

> cat temp
111111
1111
11
1
> cat temp2
122221
2222
22
2
> Perl -lne 'BEGIN{open(A,"temp");@f=<A>;}print;if(/2222/){print @f}' temp2
122221
111111
1111
11
1

2222
111111
1111
11
1

22
2
> 
1
Vijay