A blog post about finding stale pyc files got some of my interests, especially the conjunctive use of -exec and -print:


find . -name '*.pyc' \
-exec bash -c 'test "$1" -ot "${1%c}"' -- {} \; \
-print #stalepyc

Truth to be told and from all cases I have seen, -exec was always the last one in find command, so this blog post just broadened my narrow view of the command.

Nevertheless, the Bash blood in my vein had whispered in my ear as soon as I saw it, there is going to be a performance issue because every found pyc file costs one Bash execution. That’s costly.

You can simply pipe the output to a while loop, although this isn’t not the purpose of writing this post, but here it is:


find . -name '*.pyc' |
while read f; do test "$f" -ot "${f%c}"; echo "$f"; done

It was more than ten times faster on my computer, 0.100s down to just 0.008s. Note that I keep test builtin, but usually I’d prefer [[ ]] keyword.

Anyway, I have never had an issue with pyc files, they always get re-compiled correctly once the source is updated. So, not really sure where the issue actually comes from under what kind of situation. I guess I am not really an advanced Python coder after all.

But the comments in the blog post brought up another case, or part of, there are no sources. Not an issue, just worth finding those pyc files. Initially, I came up with


find . -name '*.pyc' \
\( \
-exec sh -c 'test ! -f "${1%c}"' -- {} \; \
-printf 'source not found: ' \
-print \
\) \
-o \
\( \
-exec sh -c 'test "$1" -ot "${1%c}"' -- {} \; \
-print \
\)

This is my first time that I have used -o (or operator) and stacked printing actions, never thought of such way to use find until now. Note that I use -f not -e, for some strange reason that only god knows, I suspect weird situation like weird.py directory would show up alongside weird.pyc file. Also note that sh is used instead of bash, there seems to be no need for using bash. I’ve checked POSIX standard the ${parameter%[word]} expansion is defined, compatibility wouldn’t be a problem.

How about pyd, pyo, or pyWhatever?


find -name '*.py?*' \
-exec sh -c 'test ! -f "${1%.py*}.py"' -- {} \; \
-printf 'source not found: ' \
-print

The *.py?* pattern matches at least one character after py, .py* in the end of the matched filename is stripped and put back only .py, so that formed source filename can be checked by test. This can be easily adapted to delete all those pyWhatever files by using -delete action, if that fits your needs.

There is always more to learn about everything you think you have known.