Search code examples
linuxposixshglob

POSIX glob -- how to match one-or-more [:digit:]


I want to iterate over foo.log and its log rotated siblings foo.1.gz and foo.2.gz from newest to oldest, with code that isn't fooled by the presence of foo.bar

Happily, logrotate names things such that newest-to-oldest and alphabetical are the same ordering.

My original attempt was for f in $(ls -t foo.*); do ... but shellcheck said Iterating over ls output is fragile. Use globs. [SC2045]. Plus this code matches foo.bar which is not desired.

But how do you match an arbitrary number of digits with a glob pattern? (Or is this not supported?) I only know how to list each number of digits explicitly. For example, the following handles 1 and 2 digit numbers and correctly excludes foo.1bar.gz but doesn't handle foo.123.gz (and I'm not doing the right thing to cause globing to take place!)

for f in foo.log foo.[[:digit:]].gz foo.[[:digit:]][[:digit:]].gz; do ...

I could assume that no one keeps over 100 log rotated siblings, but I'd prefer not to.

Looking for a POSIX compliant solution...

Edit: the logrotate conf compresses for some files and doesn't compress for others. So not all siblings end in .gz.


Solution

  • A glob pattern is not a regular expression. See glob(7) for the syntax.

    There is no glob pattern to match a series of digits. You can get close with foo.[0-9]*.gz. If that picks up some names you don't want, you can filter them out with a regex, maybe something like:

    echo foo.[0-9]*.* | tr ' ' \\n | grep -E '[.][0-9]+([.]gz)?$' 
    

    You can probably use a glob pattern and rely on the shell for ordering, given the constraints you presented. You can check if your shell renders filenames for a glob pattern in sorted order with sort:

    echo foo.[0-9]*.gz | tr \  \\n | sort -c
    

    But it's also OK to parse the output of ls -t unless you're being extremely rigorous. The guidance from shellcheck is good advice: many people seem to want to parse ls output when a simple glob would do, and to depend on ls to behave the same way across different systems is to invite error. That said, you're only asking for ls to sort the filenames by time, producing a single column of output. Anything else you might do would be more error-prone.