Me gustaría obtener la lista de todos los directorios que contienen documentos HTML, terminados .htm
o .html
ignorando mayúsculas o minúsculas.
Yo he tratado:
find / -type d -ls | tr -s [:blank:] | cut -d ' ' -f 11 | grep -i -e "*.htm" -e "*.html"
Pero solo enumera directorios, y necesito enumerar el contenido de esos directorios y no sé cómo.
Luego lo intenté:
find / -type d -exec ls -l {} \; | tr -s [:blank:] | cut -d ' ' -f 9 | grep -i -e ".htm" -e ".html"
Y sí los encuentra, pero ¿cómo imprimo los directorios donde están?
Respuesta1
A continuación se muestran algunos comandos posibles, incluida una salida de ejemplo.
Lo más sencillo:
$ find / -iname "*.htm*"
foo/a.HTM
foo/b.HTML
foo/b.html
foo/x.htmx
foo/a.htm
bar/a.htm
-iname
significa encontrar archivos que coincidan con glob y no distinguir entre mayúsculas y minúsculas. El problema es que el globo *.htm*
también lo encuentra htmx
.
Para evitar que lo encuentren, htmx
debe dividir el globo:
$ find / -iname "*.htm" -o -iname "*.html"
foo/a.HTM
foo/b.HTML
foo/b.html
foo/a.htm
bar/a.htm
O use grep que puede usar expresiones regulares:
$ find / | grep -i "\.html*$"
foo/a.HTM
foo/b.HTML
foo/b.html
foo/a.htm
bar/a.htm
Tenga en cuenta que la expresión regular es diferente a glob. Especialmente el punto ( .
) y la estrella ( *
) tienen significados muy diferentes en glob y regex.
Verhttps://en.wikipedia.org/wiki/Glob_(programming)#Compared_to_regular_expressionspara más información.
Respuesta2
Usando zsh
:
setopt extendedglob nullglob
for pathname in /**/*(/e{'[[ -n $REPLY/(#i)*.htm(l#)(#q.) ]]'}); do
printf '%s:\n' $pathname
ls -l $pathname
done
Esto imprime el nombre de ruta de cada directorio que contiene cualquier archivo normal cuyo nombre termine con .htm
o .html
(independientemente del caso), seguido de la ls -l
salida de ese directorio.
El bucle recorre cada directorio dentro o debajo /
que contiene un archivo HTML. Lo hace utilizando el /**/*
globo que, por sí solo, coincide con todo en toda la /
jerarquía de directorios. Esta lista se filtra para que solo contenga nombres de rutas de directorio mediante el /
calificador global (la inicial /
en el primer paréntesis), y la lista se filtra aún más para contener solo aquellas entradas que [[ -n $REPLY/(#i)*.htm(l#)(#q.) ]]
son verdaderas. Esta expresión, donde $REPLY
se encuentra uno de los nombres de ruta del directorio que se examinan, será verdadera si un directorio contiene al menos un archivo normal con un sufijo de nombre de archivo .htm
o .html
(sin distinguir entre mayúsculas y minúsculas).
La e{...}
parte del patrón globular probablemente podría escribirse de manera más sucinta.
Usando 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
Esto utiliza la globstar
opción de shell para habilitar el uso del **
patrón global (habilitado de forma predeterminada en el zsh
shell). Itera sobre todos los nombres de rutas de directorio en toda la jerarquía de directorios desde /
abajo e intenta expandir el *.htm?(l)
global en cada directorio (esto coincide con los archivos HTML que nos interesan). Si la primera coincidencia de este globo es un archivo normal, o un enlace simbólico a uno, entonces ls -l
se genera el nombre de la ruta del directorio y el listado.
Si es posible que tengasdirectorioscon el sufijo .htm
on .html
filename, tendrías que probar las coincidencias de la expansión dentro del bucle en un bucle separado, solo para asegurarte de capturar cualquier archivo normal (o enlaces simbólicos a archivos normales) con los sufijos 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
Eliminé la nullglob
opción Shell en esta variación porque ya no dependemos de ella.
En el sh
shell POSIX no tienes acceso al **
glob, por lo que tendrías que usarlo find
para generar los nombres de ruta del directorio para el bucle:
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 {} +
Aquí, find
actúa como una especie de generador de nombres de rutas para el sh -c
script incrustado y lo alimenta con nombres de rutas de directorios.
El sh -c
script hace más o menos lo que bash
hace la segunda variación de la respuesta, es decir, itera sobre la expansión del globo que debería coincidir con los nombres deseados, probando cada nombre para ver si es un archivo normal (o un enlace simbólico a uno). Una vez que encuentra un archivo, imprime el nombre de la ruta del directorio seguido de la ls -l
salida.
Respuesta3
Yo sugeriría usar
find / '(' -iname '*.htm' -o -iname '*.html' ')' -printf '%h\n' | uniq | xargs -r -d '\n' ls -l
La primera parte, find / '(' -iname '*.htm' -o -iname '*.html' ')' -printf '%h\n'
busca todos los archivos que terminan en .htm
mayúsculas .html
o minúsculas (usando patrones globales) e imprime el directorio ( %h
) para cada archivo encontrado, un directorio por línea.
Debido a la forma en que find
escanea los directorios, se enumeran uno o más directorios idénticos consecutivos; uniq
conserva sólo uno de cada uno.
Finalmente, le enviamos la lista de directorios xargs
, diciéndole que no ejecute un comando sin ningún directorio -r
y que el separador sea una nueva línea -d '\n'
. El comando es ls -l
; modifica a tu gusto.
Si solo necesita la lista de directorios, no el contenido de esos directorios, elimine la xargs
parte:
find / '(' -iname '*.htm' -o -iname '*.html' ')' -printf '%h\n' | uniq