0

I am trying to use regex to match the filenames of all files in a folder and extract the suffices. However, for some reason, the regex seems not to match any file in the folder although files with such pattern exist.

check() {
    x=$1
    regex="\/tmp\/a\/b\/c\.${x}\.(.+?)"
    while IFS= read -r -d '' file; do
            if [[ "$file" =~ $regex ]]; then
                # do something
            fi
    done < <(find /tmp/a/b -type f -name "c.${x}.*" -print0)
}

Am I missing something?

Thanks!

Clarification #1: using find isn't a requirement.

Clarification #2: $x doesn't have any special character besides underscore.

Clarification #3: (.+?) was used because I was trying to extract it and use it later via $BASH_REMATCH[1]

My folder:

$ ll /tmp/a/b/
total 0
-rw-rw-r-- 1 hc hc 0 Jun 20 15:40 list
5
  • 1
    You seem to want to use a Perl-compatible regular expression, .+? (non-greedy .+). This is probably not supported by bash (AFAIK, it only support extended regular expressions). Also, it's unclear what $x is when you run this code and what the input data looks like, and what happens when you run it. Why do you escape the / characters? It would be easier and more efficient to incorporate the test on the pathname in the call to find with -path "/tmp/a/b/c.$x.*" or something similar and then to execute whatever it is you want to do with -exec.
    – Kusalananda
    Jun 18 '20 at 13:40
  • 1
    In fact, you should be able to get by by just adding -maxdepth 1 to the find invocations. As you already restrict the names with -name, you are then guaranteed to only get files from the /tmp/a/b directory, which is what I suppose you're after.
    – Kusalananda
    Jun 18 '20 at 13:55
  • 1
    on a side note why use while do done < <( find ..) over find .. | while do done ?
    – Archemar
    Jun 18 '20 at 14:19
  • 1
    On a side note why use find at all? for f in /tmp/a/b/c.${x}.*; do [[ -f $f ]] && echo ${f##*.} ; done
    – xenoid
    Jun 18 '20 at 14:26
  • for i in $(find /a/b/c/* -iname '*'); do someCommand "$i"; done
    – summertime
    Jun 18 '20 at 14:40
2

You mentioned that you wanted to check a particular directory rather than a tree of directories, but the RE you provided in your question does not support this requirement.

In this case you can probably dispense with find entirely. Here, the glob pattern is mostly equivalent to the RE ^/tmp/a/b/c\.$x\.([^/]+?)$ (with the exception of any RE special characters in $x), but this isn't quite the same as your own unbounded RE /tmp/a/b/c.$x\.(.+?).

Please clarify in your question if I have mis-interpreted the intent of your own RE and I'll readdress that here.

check() {
    local file
    for file in /tmp/a/b/c."$1".?*
    do
        if [[ -f "$file" ]]
        then
            # do something
            :
        fi
    done
}

If you really need to use find, try this

check() {
    find /tmp/a/b -type f -name "c.$1.?*" -print0 |
        while IFS= read -r -d '' file
        do
            # do something
            :
        done
}

With a file /tmp/a/b/c.1.d_e, calling check 1 with either suggestion will reach the : statement; you can verify this by replacing it with echo "Found $file"

Finally, since you mentioned in your question that you had been using an RE comparision to extract the last part of the match, you can also get that like this

# Assume $file is valid
tail="${file##*/c."$1".}"
echo "Last part of the path is $tail"
5
  • Thanks for your answer. You did't misinterpret anything. Just that my question wasn't clear enough. I added clarifications to my question. Thanks.
    – HCSF
    Jun 19 '20 at 7:41
  • I tried your first solution, and added printf "checking $file\n" right before if statement and it printed checking /tmp/a/b/c.1.*? when called check 1. I was expecting $file should be /tmp/a/b/c.1.d_e as I have such file there.
    – HCSF
    Jun 19 '20 at 8:01
  • 1
    @HCSF that precise example works for me. Except that you shouldn't interpolate variables into a printf format string. Use printf "checking %s\n" "$file" instead
    – roaima
    Jun 19 '20 at 8:49
  • You are right that I misused printf. I fixed that. However, I noticed that it prints /tmp/a/b/c.1.?* once if /tmp/a/b/ doesn't have any file matching the pattern /tmp/a/b/c.1.?*. Tho, your solution certainly works because your if statement tested $file. Any idea why the for loop condition still meets when the folder is empty? Thanks!
    – HCSF
    Jun 20 '20 at 7:51
  • 1
    That's what globs do when there's no match. In man bash search for nullglob, as in shopt -s nullglob. Here though, it's of little use to us because we still need to filter out non-files such as directories
    – roaima
    Jun 20 '20 at 8:17

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.