Bash: вставка содержимого одного файла в другой файл после шаблона
Я пытаюсь написать сценарий bash, который будет делать следующее:
- считывает содержимое первого файла (в качестве первого аргумента)
- считывает содержимое из второго файла (в качестве второго аргумента)
- находит строку во втором файле с заданным шаблоном (в качестве третьего аргумента)
- вставляет текст из первого файла во второй файл после строки шаблона.
- печатает окончательный файл на экран.
например:
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" после строки с указанным шаблоном
позаботьтесь о том, чтобы выбрать уникальный шаблон, не уверен, как он будет работать с дубликатами шаблонов, Я предполагаю, что он будет делать это только из первого