diff --git a/lib/ansible/modules/find.py b/lib/ansible/modules/find.py index 3379718130a..8ed8823f4fb 100644 --- a/lib/ansible/modules/find.py +++ b/lib/ansible/modules/find.py @@ -154,6 +154,14 @@ options: - When doing a O(contains) search, determine the encoding of the files to be searched. type: str version_added: "2.17" + limit: + description: + - Limit the maximum number of matching paths returned. After finding this many, the find action will stop looking. + - Matches are made from the top, down (i.e. shallowest directory first). + - If not set, or set to v(null), it will do unlimited matches. + - Default is unlimited matches. + type: int + version_added: "2.18" extends_documentation_fragment: action_common_attributes attributes: check_mode: @@ -227,6 +235,16 @@ EXAMPLES = r''' - '^_[0-9]{2,4}_.*.log$' - '^[a-z]{1,5}_.*log$' +- name: Find file containing "wally" without necessarily reading all files + ansible.builtin.find: + paths: /var/log + file_type: file + contains: wally + read_whole_file: true + patterns: "^.*\\.log$" + use_regex: true + recurse: true + limit: 1 ''' RETURN = r''' @@ -467,7 +485,8 @@ def main(): depth=dict(type='int'), mode=dict(type='raw'), exact_mode=dict(type='bool', default=True), - encoding=dict(type='str') + encoding=dict(type='str'), + limit=dict(type='int') ), supports_check_mode=True, ) @@ -520,6 +539,9 @@ def main(): else: module.fail_json(size=params['size'], msg="failed to process size") + if params['limit'] is not None and params['limit'] <= 0: + module.fail_json(msg="limit cannot be %d (use None for unlimited)" % params['limit']) + now = time.time() msg = 'All paths examined' looked = 0 @@ -530,7 +552,8 @@ def main(): if not os.path.isdir(npath): raise Exception("'%s' is not a directory" % to_native(npath)) - for root, dirs, files in os.walk(npath, onerror=handle_walk_errors, followlinks=params['follow']): + # Setting `topdown=True` to explicitly guarantee matches are made from the shallowest directory first + for root, dirs, files in os.walk(npath, onerror=handle_walk_errors, followlinks=params['follow'], topdown=True): looked = looked + len(files) + len(dirs) for fsobj in (files + dirs): fsname = os.path.normpath(os.path.join(root, fsobj)) @@ -596,7 +619,12 @@ def main(): r.update(statinfo(st)) filelist.append(r) - if not params['recurse']: + if len(filelist) == params["limit"]: + # Breaks out of directory files loop only + msg = "Limit of matches reached" + break + + if not params['recurse'] or len(filelist) == params["limit"]: break except Exception as e: skipped[npath] = to_text(e)