2

I would like to obtain the list of all the directories which contains HTML documents, finished .htm or .html ignoring upper or lower case.

I have tried:

find / -type d -ls | tr -s [:blank:] | cut -d ' ' -f 11 | grep -i -e "*.htm" -e "*.html"

But it only lists directories, and I need to list those directories' content and I don't know how.

I have then tried:

find / -type d -exec ls -l {} \; | tr -s [:blank:] | cut -d ' ' -f 9 | grep -i -e ".htm" -e ".html"

And it does find them, but how do I print the directories where they are?.

1

Here are some possible commands including example output

The most simple:

$ find / -iname "*.htm*"
foo/a.HTM
foo/b.HTML
foo/b.html
foo/x.htmx
foo/a.htm
bar/a.htm

-iname means to find files matching glob and be case insensitive. The problem is the glob *.htm* also finds htmx.

To prevent finding htmx you have to split the glob:

$ find / -iname "*.htm" -o -iname "*.html"
foo/a.HTM
foo/b.HTML
foo/b.html
foo/a.htm
bar/a.htm

Or use grep which can use regex:

$ find / | grep -i "\.html*$"
foo/a.HTM
foo/b.HTML
foo/b.html
foo/a.htm
bar/a.htm

Note that regex is different than glob. Especially the dot (.) and star (*) have very different meanings in glob and regex.

See https://en.wikipedia.org/wiki/Glob_(programming)#Compared_to_regular_expressions for more information.

| improve this answer | |
1

Using zsh:

setopt extendedglob nullglob
for pathname in /**/*(/e{'[[ -n $REPLY/(#i)*.htm(l#)(#q.) ]]'}); do
    printf '%s:\n' $pathname
    ls -l $pathname
done

This prints the pathname of each directory containing any regular file whose name ends with either .htm or .html (regardless of case), followed by the ls -l output for that directory.

The loop loops over every directory in or beneath / that contains a HTML file. It does this using the /**/* glob which, by itself matches everything in the whole / directory hierarchy. This list is filtered to only contain directory pathnames by the / glob qualifier (the initial / in the first parenthesis), and the list is further filtered to only contain those entries for which [[ -n $REPLY/(#i)*.htm(l#)(#q.) ]] is true. This expression, where $REPLY is one of the directory pathnames being examined, will be true if a directory contains at least one regular file with either a .htm or .html filename suffix (case insensitively).

The e{...} part of the globbing pattern could probably be written more succinctly.


Using 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

This uses the globstar shell option to enable the use of the ** globbing pattern (enabled by default in the zsh shell). It iterates over all directory pathnames in the whole directory hierarchy from / down and tries to expand the *.htm?(l) glob in each directory (this matches the HTML files we're interested in). If the first match of this glob is a regular file, or a symbolic link to one, then the directory pathname and the ls -l listing is outputted.

If you may have directories with the .htm on .html filename suffix, you would have to test the matches of the expansion inside the loop in a separate loop, just to make sure you catch any regular files (or symbolic links to regular files) with the HTML suffixes:

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

I've deleted the nullglob shell option in this variation since we do no longer depend on it.


In the POSIX sh shell you don't have access to the ** glob, so you would have to use find to generate the directory pathnames for the loop:

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 {} +

Here, find acts like a sort of pathname generator for the embedded sh -c script and feeds it with pathnames of directories.

The sh -c script does pretty much what the 2nd bash variation of the answer does, i.e. it iterates over the expansion of the glob that should match the wanted names, testing each name to see whether it's a regular file (or a symbolic link to one). Once it finds a file it prints the directory pathname followed by the ls -l output.

| improve this answer | |
0

I'd suggest using

find / '(' -iname '*.htm' -o -iname '*.html' ')' -printf '%h\n' | uniq | xargs -r -d '\n' ls -l

The first part, find / '(' -iname '*.htm' -o -iname '*.html' ')' -printf '%h\n', finds all files that end with .htm or .html in upper or lower case (using glob patterns), and prints the directory (%h) for each such file found, one directory per line.

Because of how find scans directories, there are one or more consecutive identical directories listed; uniq retains only one of each.

Finally, we feed the list of directories to xargs, telling it to not run a command without any directories -r, and that the separator is a newline -d '\n'. The command is ls -l; modify to your liking.

If you only need the list of directories, not those directories contents, drop the xargs part:

find / '(' -iname '*.htm' -o -iname '*.html' ')' -printf '%h\n' | uniq
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.