How to use fnmatch from a shell?

2019-03-03 22:49发布

In a generic shell script, I would like to use shell pattern matching to filter the lines of a text file.

I have a list of file names in files.txt:

file1.txt
file2.sh
file3.png

And I have a list of patterns in patterns.txt:

other_file.txt
file2.*

If I would have regular expressions in patterns.txt, I could do this:

$ grep -v -f patterns.txt files.txt

But I would like to use shell globbing patterns. I found the C function fnmatch but no shell/unix command to use it.

标签: shell glob
1条回答
放荡不羁爱自由
2楼-- · 2019-03-03 23:27

OK, this is going to be really unperformant, as POSIX sh does not even have arrays (which I would have used for caching the patterns):

while IFS= read -r filename; do
    hasmatch=0
    while IFS= read -r pattern; do
        case $filename in ($pattern) hasmatch=1; break ;; esac
    done <patterns.txt
    test $hasmatch = 1 || printf '%s\n' "$filename"
done <files.txt

If you don’t need the positional arguments ($1, $2, …) you can abuse those for pattern caching though:

saveIFS=$IFS; IFS='
'; set -o noglob
set -- $(cat patterns.txt)
IFS=$saveIFS; set +o noglob
while IFS= read -r filename; do
    hasmatch=0
    for pattern in "$@"; do
        case $filename in ($pattern) hasmatch=1; break ;; esac
    done
    test $hasmatch = 1 || printf '%s\n' "$filename"
done <files.txt

Be careful about whitespace there though: we set IFS to a literal newline character and nothing else, i.e. IFS='Enter'.

I’ve tested this with your dataset plus a few additions (like a a b* pattern, to test whitespace behaviour), and it seems to work for me according to the spec in the OP.

查看更多
登录 后发表回答