Дублировать и заменить шаблон в текстовом файле

man rsync:

  -q, --quiet
          This  option  decreases  the amount of information you are given
          during the transfer, notably  suppressing  information  messages
          from  the  remote  server.  This  option is useful when invoking
          rsync from cron.
2
15.05.2017, 01:58
6 ответов

Комплекс bash + sed решение:

foobar_replacer.sh скрипт:

#!/bin/bash
head -n1 "$2"  # print the first line

while read -r line
do
    sed '1d;$d;{s/^foo bar$/'"$line"'/g}' "$2"        
done < "$1"

tail -n1 "$2" # print the last line

Использование :

bash foobar_replacer.sh foobar.txt input.txt

Вывод:

some text …
% BEGIN
blabla
2 3
blabla
blabla
% END
% BEGIN
blabla
8 9
blabla
blabla
% END
% BEGIN
blabla
1 2
blabla
blabla
% END
some text …

sed подробности команды:

1d;$d; - удалить первую и последнюю строку из input.txt

s/^foo bar$/'"$line"'/g - заменить строку, содержащую foo bar, на следующий элемент $line из foobar.txt

0
27.01.2020, 22:03

Попробуйте это:

while read line; do awk -v f="$line" '{gsub(/foo bar/, f)} 1' input; done <foobar.txt

Это читает строку за строкой из foobar.txt. Для каждой строки в foobar.txt считывается файл input и строка из foobar.txt. заменяется для каждого вхождения foo bar.

Как это работает

  • при чтении строки; do

    Это запускает цикл while, который читает строки из foobar.txt.

  • awk -vf="$line" '{gsub(/foo bar/, f)} 1' input

    Это читает файл input и заменяет в $line везде, где встречается foo bar.

    Более подробно:

    • -v f="$line"

      Это создает переменную awk f, значением которой является содержимое переменной оболочки line.

    • gsub(/foo bar/, f)

      Для каждой строки, которую awk считывает, ищет вхождения регулярного выражения foo bar и подставляет значение f

    • 1

      Это сокращение от awk для печати строки.

    Причина использования здесь awk, а не sed, заключается в том, что awk лучше обрабатывает значения переменных оболочки.

  • done

    Это сигнализирует об окончании цикла while и указывает циклу использовать файл foobar.txt в качестве стандартного ввода.

Многострочная версия

Для тех, кто любит раскидывать команды на несколько строк:

while read line
do
    awk -v f="$line" '{gsub(/foo bar/, f)} 1' input
done <foobar.txt
0
27.01.2020, 22:03
perl -nMFatal=open -e '$l = $_;
   @ARGV and open my $fh, "<", $ARGV[0];
   print +(/^%\hBEGIN/ ? $a=0 : $a++) == 1 ? $l : $_ while <$fh>;
' foobar.txt input.txt

Рабочий
  • Для каждой строки, прочитанной из файла foobar.txt, мы открываем лексический дескриптор файла $fh для файла input.txt. Причина, по которой он должен быть лексическим, заключается в том, что он закрывается сам по себе, когда считывается следующая строка ввода из foobar.txt.
  • Мы инициализируем счетчик $a, когда видим % Строка BEGIN в файле input.txt. И через 1 строчку после этого заменяем строчку в input.txt на строчку из foobar.txt.
  • Порядок аргументов следующий: foobar.txt и затем input.txt.
  • Мы включили прагму Fatal.pm, которая автоматически обрабатывает ошибки при открытии файлов.
Результаты
some text --
% BEGIN
blabla
2 3
blabla
blabla
% END
some text --
some text --
% BEGIN
blabla
8 9
blabla
blabla
% END
some text --
some text --
% BEGIN
blabla
1 2
blabla
blabla
% END
some text --
1
27.01.2020, 22:03

Вот способ сделать это на чистом awk, используя getline:

awk '
  /% BEGIN/ {
    s = 1;
  }

  s == 1 {
    b = b == "" ? $0 : b ORS $0
  }

  /% END/ {
    while ((getline repl < "foobar.txt") > 0) {
      tmp = b;
      sub(/foo bar/, repl, tmp);
      print tmp;
    }
    b = "";
    s = 0;
    next;
  }

  s == 0 {
    print;
  }' input

С помощью GNU awk вы можете сделать замену без временного использования – используя gensub:

gawk '
  /% BEGIN/ {
    s = 1;
  }

  s == 1 {
    b = b == "" ? $0 : b ORS $0
  }

  /% END/ {
    while ((getline repl < "foobar.txt") > 0) {
      print gensub(/foo bar/, repl, 1, b);
    }
    b = "";
    s = 0;
    next;
  }

  s == 0 {
    print;
  }' input

Тестирование :

$ gawk '
>   /% BEGIN/ {s = 1;}
>   s == 1 {b = b == "" ? $0 : b ORS $0}
>   /% END/ {while ((getline repl < "foobar.txt") > 0) {print gensub(/foo bar/, repl, 1, b);} s = 0; next;}
>   s == 0 {print}' input
some text …
% BEGIN
blabla
2 3
blabla
blabla
% END
% BEGIN
blabla
8 9 
blabla
blabla
% END
% BEGIN
blabla
1 2
blabla
blabla
% END
some text …
2
27.01.2020, 22:03
sed -e '
   1{
      :loop
         N
      /\n\.$/!bloop
      s///;h
      N;s/.*\n//
   }

   G
   y/\n_/_\n/
   s/^\([^_]*\)_\(.*_% BEGIN_[^_]*_\)[^_]*/\2\1/
   y/\n_/_\n/
' input.txt foobar.txt

Работа

  • В этом методе порядок аргументов следующий: :input.txt, затем foobar.txt
  • Поскольку POSIX sedне имеет представления о том, когда заканчивается один файл и начинается следующий, нам нужно либо добавить eof Различение -, скажем, ., ИЛИ основываясь на типе данных в двух файлах. файлов, чтобы определить, в каком файле мы находимся. В нашем случае я выбираю первый метод.
  • Сначала мы сохраняем файл input.txt в резервной области целиком.
  • Затем для каждой строки, прочитанной из файла foobar.txt, мы добавляем к ней пространство для хранения, а затем заменяем вторую строку после строки % BEGINв пространстве шаблонов на первую строку. Примечание :У нас есть то, что multiline pattern space, что такое...\n...\n...\n...
0
27.01.2020, 22:03

bash скрипт с использованием sed. Использование :./search_and_replace.sh < input.txt, результат будет в новом output.txtфайле

#!/bin/bash

begin_str="% BEGIN"
end_str="% END"
pattern="foo bar"
write_to_var_flag=0
output_file=output.txt
foobar_file=foobar.txt
begin_to_end_block_var=""

# clean output file if it exist, else create it
> "$output_file"

function read_foobar_file () {
    while read -r line; do
        echo -ne "$begin_to_end_block_var" | sed "s/$pattern/$line/" >> "$output_file"
    done < "$foobar_file"
}

while read -r line; do
    if  [ "$line" == "$begin_str" ]; then
        write_to_var_flag=1
    fi

    if (( $write_to_var_flag )); then
        begin_to_end_block_var+="$line\n"
    else
        echo "$line" >> "$output_file"
    fi

    if [ "$line" == "$end_str" ]; then
        read_foobar_file 
        write_to_var_flag=0
    fi
done
0
27.01.2020, 22:03

Теги

Похожие вопросы