Вставка /textline перед шаблоном, а не непосредственно перед

Учитывая, что вы хотите удалить ~~adt(something)~~и что ~~(something different)~~может быть в других строках (, не показанных в вопросе):

$ sed 's/~~adt[^~]*~~//g' file.in >file.out

Для заданных данных будет сгенерировано

478|14395189_p0.jpg
479|44836628_p0.jpg
480|Miku_Collab_2_by_Luciaraio.jpg

Изменив adtпоследней строки на xxx, команда генерирует

478|14395189_p0.jpg
479|44836628_p0.jpg
480|~~xxx00000aae~~Miku_Collab_2_by_Luciaraio.jpg

Шаблон ~~adt[^~]*~~будет соответствовать всем вхождениям ~~adt, за которыми следует любое количество символов, кроме ~, а затем снова ~~. /gв конце гарантирует, что все такие совпадения в каждой строке будут удалены.

1
20.09.2019, 00:13
3 ответа

Предположим, что вы хотите сказать:

У меня есть длинный файл с несколькими счетами. Каждый счет начинается с текста 70 spaces and a 1. Мне нужно вставить новый контент за 9 строк до этого для каждого счета

Тогда вам нужно накопить 10 строк. Когда текущая строка соответствует началу счета-фактуры , вставьте в первую строку новый текст.

На практике это приводит к тому, что:

sed -e '1{N;N;N;N;N;N;N;N}' -e 'N;l;/\n \{70\}1$/{iNew content here' -e 'P};P;D' file

Или, в полной форме (комментарии не работают в некоторых реализациях sed):

sed '1{                              # (only) on the first line
         N;N;N;N;N;N;N;N             # accumulate 9 lines (first one plus 8 more).
      }
     N                               # On every line,also accumulate that line 
     /\n \{70\}1$/{                  # If buffer ends ($) in 70 spaces and a "1"
i\
New content added here               # insert the content of this line at
                                     # the start of the buffer (10 lines above).
                       P             # and then print it.
                  }
     P;D                             # close the N cycle above by
                                     # printing and deleting one line
    ' file                           # On the selected file.

Решение awkможет использовать преимущества установки RS на строку, которая отмечает начало счета. В этом случае установка FS на символ новой строки разбивает каждую строку на поле, а $ (NF -9 )будет ссылаться на строку на 9 строк выше:

awk -v ln=9 '
              ( NF < ln ){ print; next };           # not enought fields?
                                                    # include
                         { $(NF-ln) = "New Text to include" newln $(NF-ln);
                           print                    # and print
                         };
              BEGIN{ breakln = sprintf("%71s",1);   # 70 spaces and a 1
                     newln   = sprintf("\n");       # a newline
                     RS      = breakln newln;       # set the Record Separator
                     FS      = "\n";                # set the Field separator
                     OFS     = FS;                  # print what got removed
                     ORS     = RS                   # print what got removed
                   }
            ' file

Или, альтернативно, (решение оболочки/awk):

breakln="$(printf '%71s' 1)";
newl=$'\n';
awk '  (NF<ln){print;next};
              { $(NF-ln)="New Text to include\n"$(NF-ln); print}' \
    ln=10 \
    RS="$breakln$newl" \
    FS="$newl" \
    OFS="$newl" \
    ORS="$breakln$mewl" \
    file
2
27.01.2020, 23:22

Вот решение -редактора с поддержкой сценариев. Идея состоит в том, чтобы узнать, сколько счетов в файле, основываясь исключительно на количестве строк «(70 пробелов )1» в файле. Затем сценарий повторяется столько раз и выводит команды для ed. Цикл выводит достаточно команд, чтобы изменить каждую «9-ю строку перед той, в которой есть ' (70 пробелов )1 '», чтобы вместо этого была пунктирная линия обрезки. Замените текст -----8<-----чем угодно, кроме голой начальной точки (, которую edиспользует для обозначения конца строки замены. Если мы находимся в середине цикла по счетам (i < count), пропустите 10 строк вперед после внесения изменений, чтобы мы не обнаружили страницу, которую мы только что вырезали. Если мы завершили цикл (i == count), распечатайте команды ed «записать и выйти». Весь этот вывод printf/echo поступает в конвейер, который edсчитывается как ввод. Опция -sпредназначена для «тихого» режима.--edне будет сообщать о количестве прочитанных или записанных байтов.

#!/usr/bin/bash
count=$(grep -c '^                                                                      1' input)
for((i=1; i<=count; i++))
do
  printf '%s\n' '/^                                                                      1/-9c'
  printf '%s\n' '-----8<-----' '.'
  [[ $i < $count ]] && printf '%s\n' '+10'
  [[ $i == $count ]] && echo wq
done | ed -s input
0
27.01.2020, 23:22

С awk+tac:

tac file | awk -v delim="--split page here--" '{
  if (nextnr=="" && $1~/^[0-9]+$/ && $0=="                                                                      "$1) {
     nextnr=NR+9  # pagenr found, remember next position
  }
  else if (NR==nextnr) {
     $0=delim     # overwrite line with delimiter
     nextnr=""    # reset
  }  
  print
}' | tac

Сначала переверните файл с помощью tac, чтобы мы могли искать сверху -вниз номер страницы и вставлять разделитель.

Поиск начинается в предложении if -, когда выполняются эти условия.:

  • nextnrравно (изначально )не установлено(nextnr==""). Это переменная, содержащая номер строки следующего разделителя.
  • первое поле — число($1~/^[0-9]+$/)
  • строка содержит 70 пробелов и число

Если все три условия истинны, nextnrустанавливается на текущий номер строки (количество записей)NR + 9.

Если текущая строка является строкой разделителя (NR==nextnr), перезапишите строку разделителем и сбросьте nextnr.

Последняя строка скрипта выводит текущую строку (оригинала или перезаписывает разделитель ).

На последнем шаге выход снова реверсируется с помощью tac.

1
27.01.2020, 23:22

Теги

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