Использование sed для замены шаблона

Как я могу написать сценарий bash, чтобы сделать следующее

  1. рекурсивный поиск всех файлов в каталоге, заканчивающемся на html и htm.
  2. использование sed для поиска <body> и удаления всех строк перед этой строкой, включая строку <body>
  3. А также поиск </body> и удаление всех строк после этого, включая строку <body>.
  4. изменение должно быть не в том же файле, а в index-temp.html.

Я написал ниже, но я не могу понять, как я могу изменить весь блок после и сохранить изменение в другом файле, а не в том же файле. Должен ли я использовать if?

#!/bin/bash
input=$1
find "$input" -type f -name "*.htm" -exec sed 

Нет вопросов, и вы, вероятно, имеете в виду sed вместо sid.

Ron Nabuurs 11.04.2018 13:58

Предполагая, что ваши файлы являются (разумно) допустимым HTML, вам, вероятно, следует использовать инструмент синтаксического анализа HTML для извлечения содержимого тега <body>.

Tom Fenech 11.04.2018 14:03

например xmllint --html --xpath '//body/node()' file.htm, вероятно, даст вам то, что вы хотите.

Tom Fenech 11.04.2018 14:10
"использование sed для поиска <body> и удаление всех строк перед этой строкой, включая строку <body>" - это требование четко не определено. Открывающий тег элемента body (например, <body>) может иметь атрибуты (<body class="foo"> и т. Д.). Поиск <body> может не найти его. Также нет гарантии, что он один на линии. Удаление всей строки может оказаться слишком большим. На самом деле это не задача для sed, и вы неправильно определили ее, потому что думаете об этом в терминах sed (т.е. обрабатываете файл построчно).
axiac 11.04.2018 17:22
1
5
107
2

Ответы 2

Для одного файла команда sed будет выглядеть так:

sed '1,/<body>/d;/<\/body>/,/$/d' index.html > index-temp.html

Синтаксис:

sed 'ROWa,ROWz d' 

где ROWa - это начальный номер полотна, ROWz - где закончиться, включительно, отсчитывая от 1. $ можно использовать для LASTLINE.

Вы также можете использовать шаблоны:

sed '/PATa/,/PATz/ d' 

От шаблона PATa до шаблона PATz. И узор / линии можно смешивать.

Теперь о находке:

find "$input" -type f -name "*.htm*" -exec sed -i.temp '0,/<body>/d;/<\/body>/,/$/d' {} ";" 

изменит htm (l) -файл, но создаст резервную копию (например, index.html.temp) из оригинала.

Может, тебе так даже удобнее. В противном случае вам нужно переименовать все эти файлы, для чего потребуется другой сценарий, поскольку sed и find их самостоятельно не знают перенаправления, поэтому для этого потребуется вызов оболочки с базовым именем, что было бы альтернативным способом:

#/bin/bash
#
# justbody.sh
#
infile=$1
outfile="$(basename $infile .htm)-temp.htm"
sed '0,/<body>/d;/<\/body>/,/$/d' $infile > $outfile

а теперь вызов:

find "$input" -type f -name "*.htm" -exec ./justbody.sh {} ";" 

Я считаю, что шаблон 0, - это расширение GNU sed.

tripleee 11.04.2018 15:12

@tripleee: Ну, я объясняю, что он считается от 1, но пишу «0» (что незаметно сработало), но я исправил это.

user unknown 11.04.2018 15:15

Что делать, если <body class="foo">? А что, если <html><head><title>etc</title></head><body></body></html>, все в одной строке? Редактирование HTML - определенно не работа для sed.

axiac 11.04.2018 17:22

@axiac: echo "<html><head><title>etc</title></head><body></body></html>" | sed '0,/<body>/d;/<\/body>/,/$/d' ничего не возвращает, согласно требованиям: и удалите все строки перед этой строкой, включая строку <body> и и удаление всех строк после этого, включая строку <body>. Что вы узнали, что нарушает эти требования или какое определение линия вы используете? И вопрос был про using sed to search for <body>, а не искать body-tag. Если у вас другие требования, задайте, пожалуйста, свой вопрос.

user unknown 11.04.2018 18:29

@axiac: Я вижу, что вы попросили ItInNeed прояснить этот вопрос, и это правильный путь. Но до тех пор, пока вы не являетесь производителем указанного html-файла и лучше знаете требования, ваши выводы преждевременны. Без подтверждения того, что идея поиска по строкам и только буквально для <body> слишком узка, не стоит критиковать ответы.

user unknown 11.04.2018 18:38

Предположим, у меня есть один файл и я знаю линию тела. Что мне нужно, так это иметь несколько файлов, расположенных в папке и подпапках, а также разные файлы HTML и HTM, которые не имеют одинакового местоположения тела. Есть ли какая-нибудь команда sed, которая стирает все до <body> и все после </body>?

ItInNeed 12.04.2018 09:30

@ItInNeed: Нет, это не так. Второй последний блок кода - это скрипт, который вызывается последней строкой кода и предназначен именно для этой цели. Его можно адаптировать к абсолютным номерам строк, но здесь только первая и последняя строка являются абсолютными, теги open / close-body ищутся для каждого файла. Если имя различается между htm и html и, возможно, HTM (L), необходимо внести некоторые изменения, или была сделана вторая пара скрипт / команда. Команда find может работать с '-iname «.htm», но скрипт должен оценить конкретное расширение для файла Outfile - это один оператор if или оператор case.

user unknown 12.04.2018 09:51

Команда sed '1,/<body>/d; /<\/body>/,/$/d' удаляет из строки 1 до тега тела и от конечного тега тела до конца файла.

user unknown 12.04.2018 09:53

Извините, но я немного новичок. вы имеете в виду, что мне нужно создать два скрипта? первый - вызвать find "$ input" -type f -name "* .htm" -exec ./justbody.sh {} ";" а потом еще один скрипт "justbody.sh?"

ItInNeed 12.04.2018 12:43

Ну это нормально. Прочтите текст и постарайтесь понять, о чем он говорит. Экспериментируйте, чтобы доказать себе, что вы поняли, что для вас ново. Сделайте экспериментальные данные, чтобы попробовать на примерах. Если вы не преодолеете какое-то препятствие, не стесняйтесь спрашивать.

user unknown 12.04.2018 12:54

Я действительно провел некоторый тест только для этого оператора find "$ input" -type f -name ".htm" -exec sed -i.temp '0, / <body> / d; / <\ / body> /, / $ / d '{} ";" для тестового файла с <body style = "background-color: Yellow;"> в начале и </body> в конце, и в результате все содержимое было удалено после выполнения сценария.

ItInNeed 12.04.2018 13:07

Sed сканирует строки на предмет текстовых шаблонов. Он ничего не знает о xml, html или любом другом синтаксисе. <body> не соответствует <body style=...>. Вы можете попробовать <body[^>]*>, что означает «слово« тело »после знака« <», за которым следует произвольное количество знаков« не »и, наконец, знак«> ». Вам нужно прочитать о регулярных выражениях и пройти учебное пособие по sed, это мощная техника, и регулярные выражения очень полезны во всем мире, но, не вкладывая времени, вы далеко не уйдете.

user unknown 12.04.2018 13:41

С find ... -name "*.htm*" -exec grep "<body" {} ";" вы можете получить обзор того, как выглядят строки, которые каким-то образом соответствуют "<body", например, очень важно, чтобы тег всегда заканчивался в одной строке и ничего, что вы хотели бы сохранить, не следовало в той же строке - иначе xmllint и т. д. могут быть лучшим инструментом для работы.

user unknown 12.04.2018 13:44

как говорит @Tom Fenech:

xmllint --html --xpath '//body/node()' index.htm* > index-temp.html
  • <body> and <BODY> нацелены
  • *.html?(l) только для htm / html, но с активным extglob (по умолчанию debian)

с деталями @tripleee:

find "$input" -type f -iregex '.*\.html?' \
  -exec sh -c 'for f; do
      xmllint --html --xpath "//body/node() "$f" >"${f%.htm*}"-temp.html;
    done' _ {} +

Или find ... -exec sh -c 'for f; do xmllint --html --xpath "//body/node() "$f" >"${f%.htm*}"-temp.html; done' _ {} +

tripleee 11.04.2018 15:14

... Хотя *.htm? соответствует html и htmq, но не только htm; и без кавычек ваша оболочка расширит соответствие, если в текущем каталоге есть совпадающие файлы. Вам нужен -iregex '.*\.html?' или что-то в этом роде с кавычками.

tripleee 11.04.2018 16:07

Я должен использовать sed и не использовать xmllint

ItInNeed 12.04.2018 09:27

Другие вопросы по теме