Bash: вставка содержимого одного файла в другой файл после шаблона

Я пытаюсь написать bash script, который будет делать следующее:

  • считывает содержимое из первого файла (в качестве первого аргумента)
  • читает содержимое из второго файла (в качестве второго аргумента)
  • находит строку во втором файле с заданным шаблоном (в качестве третьего аргумента)
  • вставляет текст из первого файла во второй файл после строки шаблона.
  • выводит окончательный файл на экран.

Например:

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

Ответ 1

Вам нужны пробелы вокруг оператора =~. Для сравнения:

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

Это потому, что первое выражение, по существу, оценивается как "Является ли эта строка пустой?"

Кроме того, OP-код использует небольшую тильду, а не тильда.

Тем не менее, вы можете легко избавиться от внутреннего цикла. Просто замените бит while read -r line2 на cat -- "$second_filename".

Ваш последний echo $line является правильным только в том случае, если файл не заканчивается символом новой строки (стандартный с помощью инструментов * nix). Вместо этого вы должны использовать while read -r line || [[ $line ~= '' ]]. Это работает с или без новой строки в конце.

Кроме того, Использовать больше комментариев.

Ответ 2

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

Ответ 3

Использование awk также работает.

Вставить перед маркером ### ### line:

// 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

Вставить после маркера ### ### line:

// 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

Чтобы заменить маркер ### ###:

// 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.

Ответ 4

Это должно работать:

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
> 

Ответ 5

Я использую sed, как это, и он работал как прелесть

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

Что он делает, это вставить "filetoinsert" в "filetobeinserted" после строки с указанным шаблоном

Позаботьтесь о выборе уникального шаблона, не уверен, как он будет работать с повторяющимися шаблонами, я предполагаю, что он сделает это только с первого раза