Я хотел бы получить список всех каталогов, содержащих HTML-документы, завершенные .htm
или .html
игнорирующие заглавные или строчные буквы.
Я пытался:
find / -type d -ls | tr -s [:blank:] | cut -d ' ' -f 11 | grep -i -e "*.htm" -e "*.html"
Но он выводит только список каталогов, а мне нужно вывести список содержимого этих каталогов, но я не знаю как.
Затем я попробовал:
find / -type d -exec ls -l {} \; | tr -s [:blank:] | cut -d ' ' -f 9 | grep -i -e ".htm" -e ".html"
И он их находит, но как мне распечатать каталоги, где они находятся?
решение1
Вот некоторые возможные команды, включая пример вывода.
Самый простой:
$ find / -iname "*.htm*"
foo/a.HTM
foo/b.HTML
foo/b.html
foo/x.htmx
foo/a.htm
bar/a.htm
-iname
означает находить файлы, соответствующие glob и быть нечувствительными к регистру. Проблема в том, что glob *.htm*
также находит htmx
.
Чтобы предотвратить обнаружение, htmx
вам придется разделить шар:
$ find / -iname "*.htm" -o -iname "*.html"
foo/a.HTM
foo/b.HTML
foo/b.html
foo/a.htm
bar/a.htm
Или используйте grep, который может использовать регулярные выражения:
$ find / | grep -i "\.html*$"
foo/a.HTM
foo/b.HTML
foo/b.html
foo/a.htm
bar/a.htm
Обратите внимание, что regex отличается от glob. Особенно точка ( .
) и звездочка ( *
) имеют совершенно разные значения в glob и regex.
Видетьhttps://en.wikipedia.org/wiki/Glob_(программирование)#Сравнение_с_регулярными_выражениямиЧтобы получить больше информации.
решение2
С использованием zsh
:
setopt extendedglob nullglob
for pathname in /**/*(/e{'[[ -n $REPLY/(#i)*.htm(l#)(#q.) ]]'}); do
printf '%s:\n' $pathname
ls -l $pathname
done
Это выводит путь к каждому каталогу, содержащему любой обычный файл, имя которого заканчивается на «» .htm
или «» .html
(независимо от регистра), а затем ls -l
вывод для этого каталога.
Цикл проходит по каждому каталогу, /
содержащему файл HTML, в котором он находится. Он делает это с помощью /**/*
glob, который сам по себе соответствует всему во всей /
иерархии каталогов. Этот список фильтруется, чтобы содержать только имена путей каталогов с помощью /
квалификатора glob (инициал /
в первых скобках), и список дополнительно фильтруется, чтобы содержать только те записи, для которых [[ -n $REPLY/(#i)*.htm(l#)(#q.) ]]
является истинным. Это выражение, где $REPLY
— одно из проверяемых имен путей каталогов, будет истинным, если каталог содержит хотя бы один обычный файл с суффиксом .htm
или .html
имени файла (без учета регистра).
Часть e{...}
шаблона подстановки, вероятно, можно было бы записать более кратко.
С использованием bash
:
shopt -s globstar nullglob extglob nocaseglob
for pathname in /**/*/; do
set -- "$pathname"/*.htm?(l)
if [[ -f $1 ]]; then
printf '%s:\n' "${pathname%/}"
ls -l "$pathname"
fi
done
Это использует globstar
опцию оболочки для включения использования **
шаблона подстановки (включенного по умолчанию в zsh
оболочке). Он перебирает все пути к каталогам во всей иерархии каталогов снизу /
вверх и пытается расширить глоб в каждом каталоге (это соответствует HTML-файлам, которые нас интересуют). Если первое совпадение этого глобуса является обычным файлом или символической ссылкой на него, то выводится *.htm?(l)
путь к каталогу и список .ls -l
Если у вас естькаталогис суффиксом .htm
имени .html
файла вам придется проверять совпадения расширения внутри цикла в отдельном цикле, просто чтобы убедиться, что вы перехватываете любые обычные файлы (или символические ссылки на обычные файлы) с суффиксами HTML:
shopt -s globstar extglob nocaseglob
for pathname in /**/*/; do
for match in "$pathname"/*.htm?(l); do
if [[ -f $match ]]; then
printf '%s:\n' "${pathname%/}"
ls -l "$pathname"
break
fi
done
done
Я удалил nullglob
опцию shell в этом варианте, так как она нам больше не нужна.
В оболочке POSIX sh
у вас нет доступа к **
glob, поэтому вам придется использовать find
для генерации путей к каталогам для цикла:
find / -type d -exec sh -c '
for pathname do
for match in "$pathname"/*.[hH][tT][mM] "$pathname"/*.[hH][tT][mM][lL] ; do
if [ -f "$match" ]; then
printf "%s:\n" "${pathname%/}"
ls -l "$pathname"
break
fi
done
done' sh {} +
Здесь find
действует как своего рода генератор путей для встроенного sh -c
скрипта и передает ему пути к каталогам.
Скрипт sh -c
делает примерно то же, что и 2-й bash
вариант ответа, то есть он перебирает расширение glob, которое должно соответствовать искомым именам, проверяя каждое имя, чтобы увидеть, является ли оно обычным файлом (или символической ссылкой на него). Как только он находит файл, он печатает путь к каталогу, а затем вывод ls -l
.
решение3
Я бы предложил использовать
find / '(' -iname '*.htm' -o -iname '*.html' ')' -printf '%h\n' | uniq | xargs -r -d '\n' ls -l
Первая часть, find / '(' -iname '*.htm' -o -iname '*.html' ')' -printf '%h\n'
, находит все файлы, которые заканчиваются на .htm
или .html
в верхнем или нижнем регистре (используя шаблоны glob), и выводит каталог ( %h
) для каждого такого найденного файла, по одному каталогу на строку.
Из-за особенностей find
сканирования каталогов в списке имеется один или несколько последовательных идентичных каталогов; uniq
сохраняется только один из каждого каталога.
Наконец, мы передаем список каталогов в xargs
, сообщая ему, что не следует запускать команду без каких-либо каталогов -r
, и что разделителем является новая строка -d '\n'
. Команда ls -l
; измените по своему вкусу.
Если вам нужен только список каталогов, а не их содержимое, опустите эту xargs
часть:
find / '(' -iname '*.htm' -o -iname '*.html' ')' -printf '%h\n' | uniq