78
import glob

list = glob.glob(r'*abc*.txt') + glob.glob(r'*123*.txt') + glob.glob(r'*a1b*.txt')

for i in list:
  print i

This code works to list files in the current folder which have 'abc', '123' or 'a1b' in their names.

How would I use one glob to perform this function?

martineau
  • 119,623
  • 25
  • 170
  • 301
user1561868
  • 791
  • 1
  • 5
  • 6
  • I don't think you can. python's glob doesn't support `{}` and I think that's pretty much the only way to do it. – mgilson Oct 23 '12 at 13:57

4 Answers4

112

The easiest way would be to filter the glob results yourself. Here is how to do it using a simple loop comprehension:

import glob
res = [f for f in glob.glob("*.txt") if "abc" in f or "123" in f or "a1b" in f]
for f in res:
    print f

You could also use a regexp and no glob:

import os
import re
res = [f for f in os.listdir(path) if re.search(r'(abc|123|a1b).*\.txt$', f)]
for f in res:
    print f

(By the way, naming a variable list is a bad idea since list is a Python type...)

Charlie
  • 8,530
  • 2
  • 55
  • 53
Schnouki
  • 7,527
  • 3
  • 33
  • 38
  • 2
    I think you meant `or` instead of `and`, didn't you ? – Emmanuel Oct 23 '12 at 14:09
  • @Emmanuel I'm not sure... OP wrote about "files which have 'abc', '123' *and* 'a1b' in thir names'. But looking at his code I'm guessing `or` would actually be a better choice. Thanks for pointing that out. – Schnouki Oct 23 '12 at 14:12
  • @user1561868 Changed :) No problem about the English, it's not my native language either. – Schnouki Oct 23 '12 at 14:17
  • 8
    res = [f for f in glob.glob("*.txt") if re.match(r'[abc|123|a1b].*', f) – Omar Sep 10 '15 at 16:21
41

I'm surprised that no answers here used filter.

import os
import re

def glob_re(pattern, strings):
    return filter(re.compile(pattern).match, strings)

filenames = glob_re(r'.*(abc|123|a1b).*\.txt', os.listdir())

This accepts any iterator that returns strings, including lists, tuples, dicts(if all keys are strings), etc. If you want to support partial matches, you could change .match to .search. Please note that this obviously returns a generator, so if you want to use the results without iterating over them, you could convert the result to a list yourself, or wrap the return statement with list(...).

Evan
  • 2,120
  • 1
  • 15
  • 20
  • 1
    re: Evan It works for me only if I add the list() to the filter() function. def glob_re(pattern, strings): return list(filter(re.compile(pattern).match, strings)) Hope it will help. – argan Jul 12 '19 at 03:23
  • 3
    @argan Various operations in python have their results lazily evaluated, and filter is one of these. There are technical reasons why lazy evaluation is useful in actual programs you are writing, but when you are programming interactively in a repl it is a constant source of annoyance. I completely understand just wrapping it in a list for interactive run-once coding, but when you are outside the land of one-liners it is best to take advantage of lazy evaluation. – Evan Jul 12 '19 at 14:33
  • @argan In order to get your program to work, you only need to feed the variables through something that takes an iterable (like list(x) as you mentioned). For example: `for path in glob_re(pattern, string): print(path)` – Evan Jul 12 '19 at 14:34
17

Here is a ready to use way of doing this, based on the other answers. It's not the most performance critical, but it works as described;

def reglob(path, exp, invert=False):
    """glob.glob() style searching which uses regex

    :param exp: Regex expression for filename
    :param invert: Invert match to non matching files
    """

    m = re.compile(exp)

    if invert is False:
        res = [f for f in os.listdir(path) if m.search(f)]
    else:
        res = [f for f in os.listdir(path) if not m.search(f)]

    res = map(lambda x: "%s/%s" % ( path, x, ), res)
    return res
SleepyCal
  • 5,739
  • 5
  • 33
  • 47
  • 1
    Should use `match` to completely match the names. Downvoting. – holdenweb Jul 06 '16 at 12:39
  • 2
    I would prefer lambda x: os.path.join(path,x) – user_na Sep 01 '19 at 21:11
  • 2
    @user_na: Better yet, that should have been done right in the `res` list-comprehension instead of looping through all the matches a second time. – martineau Mar 24 '21 at 11:09
  • @martineau indeed. Also the if/else can be done directly in the if statement of the list comprehension. But using os.path.join instead of string formating is probably the first thing I would fix. – user_na Mar 25 '21 at 13:07
1
for filename in glob.iglob(path_to_directory + "*.txt"):
    if filename.find("abc") != -1 or filename.find("123") != -1 or filename.find("a1b") != -1:
        print filename
R.Camilo
  • 11
  • 1
  • 3
    Could you add some explanations to your answer? – kvorobiev Nov 24 '17 at 15:42
  • The idea is basically to iterate over the list of names of the files in the directory and find those that have any of the text strings in their name – R.Camilo Nov 27 '17 at 01:38
  • in case you want the files that contain all the strings in their name you only have to change the "or" to "and" – R.Camilo Nov 27 '17 at 01:40