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

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

  1. считывает содержимое первого файла (в качестве первого аргумента)
  2. считывает содержимое из второго файла (в качестве второго аргумента)
  3. находит строку во втором файле с заданным шаблоном (в качестве третьего аргумента)
  4. вставляет текст из первого файла во второй файл после строки шаблона.
  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=""
    second_filename=""
    pattern=""

    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

5 ответов


вам нужны пробелы вокруг =~ оператора. Сравнить:

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

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

и OP-код использует маленький Тильда, а не Тильда.

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

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

и Использовать Больше Цитат™.


sed может сделать это без циклов. Используйте его :

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

используя 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

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

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


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

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
> 

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

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

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

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