Sed, как извлечь текст между двумя тегами, но включить его

Этот ответ содержится в следующих частях:

  • Основное использование-exec
  • Использование -execв сочетании сsh -c
  • Использование-exec... {} +
  • Использование-execdir

Основное использование-exec

Опция -execпринимает внешнюю утилиту с необязательными аргументами в качестве аргумента и выполняет ее.

Если строка {}присутствует где-либо в заданной команде, каждый ее экземпляр будет заменен текущим обрабатываемым путем (, например../some/path/FILENAME). В большинстве оболочек два символа {}не нужно заключать в кавычки.

Команду необходимо завершить с помощью ;, чтобы findзнать, где она заканчивается (, поскольку впоследствии могут быть дополнительные параметры ). Чтобы защитить ;от оболочки, его нужно заключать в кавычки как \;или ';', иначе оболочка увидит его как конец команды find.

Пример (\в конце первых двух строк предназначены только для продолжения строки):

find. -type f -name '*.txt'      \
   -exec grep -q 'hello' {} ';'   \
   -exec cat {} ';'

Это найдет все обычные файлы (-type f), имена которых соответствуют шаблону *.txtв текущем каталоге или ниже него.Затем он проверит, встречается ли строка helloв каком-либо из найденных файлов, используя grep -q(, который не производит никакого вывода, только статус выхода ). Для тех файлов, которые содержат строку, будет выполнено catдля вывода содержимого файла на терминал.

Каждый -execтакже действует как «проверка» на пути, найденные с помощью find, точно так же, как -typeи -name. Если команда возвращает нулевой статус выхода (, означающий «успех» ), рассматривается следующая часть команды find, в противном случае команда findпродолжается со следующим именем пути. Это используется в приведенном выше примере для поиска файлов, содержащих строку hello, но для игнорирования всех остальных файлов.

Приведенный выше пример иллюстрирует два наиболее распространенных варианта использования-exec:

  1. В качестве теста для дальнейшего ограничения поиска.
  2. Для выполнения какого-либо действия над найденным путем (обычно, но не обязательно, в конце команды find).

Использование -execв сочетании сsh -c

Команда, которую может выполнить -exec, ограничена внешней утилитой с необязательными аргументами. Использовать встроенные в оболочку -ins, функции, условные операторы, конвейеры, перенаправления и т. д. напрямую с -execневозможно, если только они не обернуты чем-то вроде дочерней оболочки sh -c.

Если требуются функции bash, используйте bash -cвместо sh -c.

sh -cзапускает /bin/shсо сценарием, заданным в командной строке, за которым следуют необязательные аргументы командной строки для этого сценария.

Простой пример использования sh -cбезfind:

sh -c 'echo  "You gave me $1, thanks!"' sh "apples"

Это передает два аргумента дочернему сценарию оболочки. Они будут помещены в $0и $1для использования сценарием.

  1. Строка sh. Это будет доступно как $0внутри скрипта, и если внутренняя оболочка выдаст сообщение об ошибке, она добавит к нему эту строку.

  2. Аргумент applesдоступен в скрипте как $1, и если бы было больше аргументов, то они были бы доступны как $2, $3и т. д. Они также были бы доступны в списке "$@"(, за исключением $0, который не будет частью "$@").

Это полезно в сочетании с -exec, так как позволяет нам создавать произвольно сложные сценарии, которые действуют на путях, найденных с помощью find.

Пример :Найти все обычные файлы, которые имеют определенный суффикс имени файла, и изменить этот суффикс имени файла на другой суффикс, где суффиксы хранятся в переменных:

from=text  #  Find files that have names like something.text
to=txt     #  Change the.text suffix to.txt

find. -type f -name "*.$from" -exec sh -c 'mv "$3" "${3%.$1}.$2"' sh "$from" "$to" {} ';'

Внутри внутреннего скрипта $1будет строкой text, $2будет строкой txt, а $3будет любым путем, найденным для нас find. Расширение параметра ${3%.$1}возьмет имя пути и удалит из него суффикс .text.

Или, используя dirname/basename:

find. -type f -name "*.$from" -exec sh -c '
    mv "$3" "$(dirname "$3")/$(basename "$3" ".$1").$2"' sh "$from" "$to" {} ';'

или, с добавлением переменных во внутренний скрипт:

find. -type f -name "*.$from" -exec sh -c '
    from=$1; to=$2; pathname=$3
    mv "$pathname" "$(dirname "$pathname")/$(basename "$pathname" ".$from").$to"' sh "$from" "$to" {} ';'

Обратите внимание, что в этом последнем варианте переменные fromи toв дочерней оболочке отличаются от переменных с теми же именами во внешнем скрипте.

Это правильный способ вызова произвольного сложного скрипта из -execс помощью find. Использование findв цикле типа

for pathname in $( find... ); do

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

См. также:


Использование-exec... {} +

;в конце можно заменить на +.Это заставляет findвыполнить данную команду с максимально возможным количеством аргументов (найденных путей ), а не по одному разу для каждого найденного пути. Строка {}должна стоять непосредственно перед +, чтобы это работало .

find. -type f -name '*.txt' \
   -exec grep -q 'hello' {} ';' \
   -exec cat {} +

Здесь findсоберет полученные пути и выполнит catмаксимально возможное количество из них одновременно.

find. -type f -name "*.txt" \
   -exec grep -q "hello" {} ';' \
   -exec mv -t /tmp/files_with_hello/ {} +

Аналогично здесь, mvбудет выполняться как можно меньше раз. Для этого последнего примера требуется GNU mvот coreutils (, который поддерживает опцию -t).

Использование -exec sh -c... {} +также является эффективным способом перебора множества путей с произвольно сложным сценарием.

Основы такие же, как и при использовании -exec sh -c... {} ';', но теперь скрипт принимает гораздо более длинный список аргументов. Их можно зациклить, зациклив "$@"внутри скрипта.

Наш пример из последнего раздела, который изменяет суффиксы имен файлов:

from=text  #  Find files that have names like something.text
to=txt     #  Change the.text suffix to.txt

find. -type f -name "*.$from" -exec sh -c '
    from=$1; to=$2
    shift 2  # remove the first two arguments from the list
             # because in this case these are *not* pathnames
             # given to us by find
    for pathname do  # or:  for pathname in "$@"; do
        mv "$pathname" "${pathname%.$from}.$to"
    done' sh "$from" "$to" {} +

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

Существует также -execdir(, реализованный в большинстве вариантов find, но не стандартный вариант ).

Это работает аналогично -execс той разницей, что данная команда оболочки выполняется с каталогом найденного пути в качестве текущего рабочего каталога и что {}будет содержать базовое имя найденного пути без его пути (но GNU findпо-прежнему будет ставить перед базовым именем префикс ./, а BSD findили sfindне будет ).

Пример:

find. -type f -name '*.txt' \
    -execdir mv -- {} 'done-texts/{}.done' \;

При этом каждый найденный*.txt-файл будет перемещен в ранее -существующий done-textsподкаталог в том же каталоге, где был найден файл . Файл также будет переименован путем добавления к нему суффикса .done. --, чтобы отметить конец параметров, необходимо здесь в тех реализациях find, которые не имеют префикса базового имени ./.Кавычки вокруг аргумента, содержащего {}not в целом, необходимы, если ваша оболочка (t)csh. Также обратите внимание, что не все findреализации будут расширяться, {}там(sfindне будут ).

Это было бы немного сложнее сделать с -exec, так как нам пришлось бы получить базовое имя найденного файла из {}, чтобы сформировать новое имя файла. Нам также нужно имя каталога из {}, чтобы правильно найти каталог done-texts.

С -execdirнекоторые вещи становятся проще.

Соответствующая операция с использованием -execвместо -execdirдолжна была бы использовать дочернюю оболочку:

find. -type f -name '*.txt' -exec sh -c '
    for name do
        mv "$name" "$( dirname "$name" )/done-texts/$( basename "$name" ).done"
    done' sh {} +

или,

find. -type f -name '*.txt' -exec sh -c '
    for name do
        mv "$name" "${name%/*}/done-texts/${name##*/}.done"
    done' sh {} +

1
02.12.2019, 18:12
1 ответ

В вашем случае гораздо проще использовать grep. Например. таким образом:

grep -o '<a href=[^<]*<\/a>'

Если между <a href>..</a>есть <, вы можете попробовать вместо этого:

grep -o '<a href=.*<\/a>'

Однако обратите внимание, что он может возвращать данные, которые вы не ожидаете для строк, в которых есть несколько <a href=...</a>вхождений в одной и той же строке.

sedне работает для вас, потому что /pattern1/,/pattern2/pуказывает sedпечатать все строки между строкой с pattern1и строкой с pattern2(, включая строки с шаблонами ).

Проблема также может быть решена с помощью sed, но это зависит от содержимого somestupidstuff(, например. одинаково во всех случаях или нет и т. д.)

3
27.01.2020, 23:29

Теги

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