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" после строки с указанным шаблоном
Позаботьтесь о выборе уникального шаблона, не уверен, как он будет работать с повторяющимися шаблонами, я предполагаю, что он сделает это только с первого раза