sed, как читать и обрабатывать файл неизвестной длины - PullRequest
3 голосов
/ 06 марта 2012

Я хочу вставить в исходный файл html размеченный (в html) другой текстовый файл неизвестной длины, но всегда не менее двух строк. Я собирался использовать m4, но «включить» читает весь файл AFAIK. Итак, на седе ...

Как только я нашел шаблон, который указывает начало точки вставки, первая строка будет добавлена ​​к тегам <div class=...>, а вторая аналогично (но другой класс), а затем зациклится до EOF, затем остальные исходный файл выводится.

Поиск точки вставки в порядке, как и печать оставшейся части исходного файла. У меня проблема с циклом sed для чтения в текстовом файле до тех пор, пока это не будет сделано.

Пример ввода

title1
author1
title2
author2
...
titleN
authorN

Желаемый вывод

<!-- above here is source file, below is sed'ed output -->
<div class="title">
title1
</div>
<div class="author">
author1
</div>
<div class="title">
title2
</div>
<div class="author">
author2
</div>
...
<div class="title">
titleN
</div>
<div class="author">
authorN
</div>
<!-- below is rest of source file -->

Меня не слишком волнуют разрывы строк, все на одной строке - хорошо, пример - просто прояснить, что происходит. `

Я могу заставить его работать нормально с a \ <div .... и R filename и так далее с простым случаем двух или четырех строк ввода. Как только я пытаюсь использовать цикл для обработки случая с переменным количеством строк ввода, у меня не получается.

Я попытался использовать фиктивную подстановку s|^\(.+\)|\1|, чтобы проверить его с помощью T и выйти, если совпадение с образцом было пустым, но оно не работает. Моя другая попытка привела к тому, что sed вошел в бесконечный цикл.

Как проверить, успешно ли R или нет? Есть ли здесь шаблон дизайна, который мне не хватает?

(я использую GNU sed, поэтому R и T в порядке.)

Спасибо.

Ответы [ 4 ]

3 голосов
/ 06 марта 2012

Не думайте о sed только как о языке, который перебирает строки.Вы можете указать диапазон строк, сопоставив первую и последнюю строки как диапазон строк:

sed '/firstRE/,/secondRE/s/ThingsBetweenLines/ReplaceWithThis/'

Например:

[ghoti@pc ~]$ printf 'one\ntwo\nthree\nfour\nfive\n' | sed '/two/,/four/s/[ore]/_/g'
one
tw_
th___
f_u_
five
[ghoti@pc ~]$ 

Смысл в том, что sed на самом деле нехорошо вставляет целые строки, и sed не может сказать «текущий номер строки четный / нечетный».Многострочные вещи тайны и безобразны.У Gnu sed, если я помню, есть несколько многострочных обозначений, но уже поздно ночью, и я никогда не могу вспомнить, как использовать нестандартные вещи.

Поэтому я рекомендую awk.:) Его код легче читать, и он лучше подходит для такого рода задач.

awk '
  BEGIN {
    fmt="<div class=\"title\">%s</div>\n<div class=\"author\">%s</div>\n";
  }
  {
    title=$0; getline; author=$0;
    printf(fmt, title, author);
  }
'

Конечно, вы также можете сделать это в чистой оболочке:

#!/bin/sh

fmt="<div class=\"title\">%s</div>\n<div class=\"author\">%s</div>\n"

while read line; do
  if [ -z "$title" ]; then
    title="$line"
    continue
  fi
  author="$line"
  printf "$fmt" "$title" "$author"
  title=''
done

См., у меня это работает:

[ghoti@pc ~/tmp]$ printf 'title1\nauthor1\ntitle2\nauthor2\n' | ./doit
<div class="title">title1</div>
<div class="author">author1</div>
<div class="title">title2</div>
<div class="author">author2</div>
[ghoti@pc ~/tmp]$ printf 'title1\nauthor1\ntitle2\nauthor2\n' | ./doit.awk
<div class="title">title1</div>
<div class="author">author1</div>
<div class="title">title2</div>
<div class="author">author2</div>
[ghoti@pc ~/tmp]$ 
1 голос
/ 06 марта 2012

У вас есть два входных файла. Один состоит из:

some text
insertion point pattern
rest of the text

плюс список чередующихся строк заголовка и автора во втором файле.

И вывод должен быть:

some text
insertion point pattern
...alternating list of title and author <div>s
rest of the text

Я думаю, что самый простой способ справиться с этим:

  1. Обработка списка заголовков / авторов (из файла title.authors) во временный файл.
  2. Пусть sed прочитает временный файл в точке вставки.

Это переводится в схему:

tmp=${TMPDIR:-/tmp}/at.$$     # Or use mktemp command
trap "rm -f $tmp; exit 1" 0 1 2 3 13 15

sed -e 'N' \
    -e 's%\(.*\)\n\(.*\)%<div class="title">\1</div>\n<div class="author">\2</div>%' \
    title.authors > $tmp

sed "/insertion point pattern/r $tmp" main-file > output-file

rm -f $tmp
trap 0

Сведения с помощью команд trap гарантируют, что сценарий очищается после себя, если ему отправляется сигнал HUP, INT, QUIT, PIPE или TERM.

Первый скрипт sed использует N для объединения соседних строк, поэтому он дает заголовок и автора в двух строках в пространстве образца. Затем другая строка собирает материал с обеих сторон новой строки в \1 и \2, которые затем помечаются.

Второй сценарий sed определяет точку вставки, печатает эту строку, читает предварительно обработанный файл заголовков и авторов (обратите внимание на двойные кавычки, чтобы оболочка могла расширяться $tmp) непосредственно перед чтением следующей строки.

Требовать временного файла - небольшая неприятность, но при этом четко разделяются различные обязанности по «форматированию заголовка и информации об авторе» и «копированию форматированного заголовка и информации об авторе в правильное место в потоке данных.

Если вам нужен маркер HTML / XML комментариев в выводе, вы можете усложнить свой сценарий предварительной обработки с помощью:

   -e '1i\
      <!-- above here is source file, below is sed'ed output -->' \
   -e '$a\
      <!-- below is rest of source file -->'

Имейте в виду, что ведущие пробелы будут включены в вывод. Если это имеет значение, поместите весь первый скрипт в файл (title-author.sed) и используйте sed -f title-author.sed title.authors > $tmp для предварительной обработки информации:

название-author.sed

1i\
<!-- above here is source file, below is sed'ed output -->
$a\
<!-- below is rest of source file -->
N
s%\(.*\)\n\(.*\)%<div class="title">\1</div>\n<div class="author">\2</div>%

Недостатком этого является дополнительный файл - скрипт sed. Конечно, вы можете создать его на лету как другой временный файл. Мой трюк заключается в использовании:

tmp=${TMPDIR:-/tmp}/at.$$
trap "rm -f $tmp.?; exit 1" 0 1 2 3 13 15

cat > $tmp.1 <<'EOF'
1i\
<!-- above here is source file, below is sed'ed output -->
$a\
<!-- below is rest of source file -->
N
s%\(.*\)\n\(.*\)%<div class="title">\1</div>\n<div class="author">\2</div>%
EOF

sed -f $tmp.1 title.authors > $tmp.2

sed "/insertion point pattern/r $tmp.2" main-file > output-file

rm -f $tmp.?
trap 0

Изменение заключается в использовании сгенерированного временного имени в качестве префикса, а фактические временные файлы $tmp.1, $tmp.2. Очистка незначительно отличается, чтобы отразить, что может быть несколько временных файлов для удаления.

Очевидно, что вы можете настроить параметры двух входных файлов в качестве сценария и просто оставить запись сценария в стандартном выводе, чтобы можно было перенаправить его вывод в любое место, вместо принудительного ввода output-file. На самом деле это должен делать скрипт общего назначения.

1 голос
/ 06 марта 2012

Это может работать для вас (GNU sed):

cat <<! >couplet.sed
N;s/\(.*\)\n\(.*\)/<div class="title">\1<\/div><div class="author">\2<\/div>/
!
sed '/^<!-- below is rest of source file -->/e sed -f couplet.sed data' source
!-- above here is source file, below is sed'ed output -->
<div class="title">title1</div><div class="author">author1</div>
<div class="title">title2</div><div class="author">author2</div>
...
<div class="title">titleN</div><div class="author">authorN</div>
<!-- below is rest of source file -->

Необходима программа sed внутри команды sed.Это достигается с помощью команды e.

NB. Программу sed можно заменить любой командой / скриптом bash / и т. Д.

Объяснение:

  • СоздатьСценарий sed, который считывает файл данных по 2 строки за раз и создает желаемый класс div
  • Считывает исходный файл до точки вставки и затем запускает приведенный выше скрипт.Команда e вставляет выходные данные результатов запуска couplet.sed в файл данных в выходные данные sed oneliner.

Команда e может быть выполнена тремя способами:

  1. В качестве флага команды s.Который оценивает что-либо в RHS s/PATTERN/COMMAND/e
  2. В качестве отдельной команды, вставленной в выходной поток, например, 1e date
  3. Без параметров он оценивает все, что находится в пространстве шаблона.

Альтернативное решение sed:

sed -e 'N;s/\(.*\)\n\(.*\)/\/^<!-- below is rest of source file -->\/i\\<div class="title">\1<\/div><div class="author">\2<\/div>/' data |
sed -f - source
0 голосов
/ 15 августа 2016

Это не работа для sed, это работа для awk:

awk 'NR==FNR{a[NR]=$0; next} {print} /<div class=/{print a[++c]}' file1.txt file2.html
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...