diff --git a/doit/cmd_run.py b/doit/cmd_run.py
index 8a3693a..50e34b9 100644
--- a/doit/cmd_run.py
+++ b/doit/cmd_run.py
@@ -116,6 +116,18 @@ opt_pdb = {
     }
 
 
+# use ".*" as default regex for delayed tasks without explicitly specified regex
+opt_auto_delayed_regex = {
+    'name': 'auto_delayed_regex',
+    'short': '',
+    'long': 'auto-delayed-regex',
+    'type': bool,
+    'default': False,
+    'help':
+"""Uses the default regex ".*" for every delayed task loader for which no regex was explicitly defined"""
+}
+
+
 class Run(DoitCmdBase):
     doc_purpose = "run tasks"
     doc_usage = "[TASK/TARGET...]"
@@ -124,7 +136,8 @@ class Run(DoitCmdBase):
 
     cmd_options = (opt_always, opt_continue, opt_verbosity,
                    opt_reporter, opt_outfile, opt_num_process,
-                   opt_parallel_type, opt_pdb, opt_single)
+                   opt_parallel_type, opt_pdb, opt_single,
+                   opt_auto_delayed_regex)
 
 
     def __init__(self, **kwargs):
@@ -162,7 +175,7 @@ class Run(DoitCmdBase):
     def _execute(self, outfile,
                  verbosity=None, always=False, continue_=False,
                  reporter='console', num_process=0, par_type='process',
-                 single=False):
+                 single=False, auto_delayed_regex=False):
         """
         @param reporter:
                (str) one of provided reporters or ...
@@ -172,7 +185,7 @@ class Run(DoitCmdBase):
         """
         # get tasks to be executed
         # self.control is saved on instance to be used by 'auto' command
-        self.control = TaskControl(self.task_list)
+        self.control = TaskControl(self.task_list, auto_delayed_regex=auto_delayed_regex)
         self.control.process(self.sel_tasks)
 
         if single:
diff --git a/doit/control.py b/doit/control.py
index 6350b99..c60d688 100644
--- a/doit/control.py
+++ b/doit/control.py
@@ -26,9 +26,10 @@ class TaskControl(object):
                           Value: task_name
     """
 
-    def __init__(self, task_list):
+    def __init__(self, task_list, auto_delayed_regex=False):
         self.tasks = {}
         self.targets = {}
+        self.auto_delayed_regex = auto_delayed_regex
 
         # name of task in order to be executed
         # this the order as in the dodo file. the real execution
@@ -174,22 +175,51 @@ class TaskControl(object):
             # by task name
             if filter_ in self.tasks:
                 selected_task.append(filter_)
+                continue
+
             # by target
-            elif filter_ in self.targets:
+            if filter_ in self.targets:
                 selected_task.append(self.targets[filter_])
-            else:
-                # if can not find name check if it is a sub-task of a delayed
-                basename = filter_.split(':', 1)[0]
-                if basename in self.tasks:
-                    loader = self.tasks[basename].loader
-                    loader.basename = basename
-                    self.tasks[filter_] = Task(filter_, None, loader=loader)
-                    selected_task.append(filter_)
+                continue
+
+            # if can not find name check if it is a sub-task of a delayed
+            basename = filter_.split(':', 1)[0]
+            if basename in self.tasks:
+                loader = self.tasks[basename].loader
+                loader.basename = basename
+                self.tasks[filter_] = Task(filter_, None, loader=loader)
+                selected_task.append(filter_)
+                continue
+
+            # check if target matches any regex
+            import re
+            tasks = []
+            for task in list(self.tasks.values()):
+                if task.loader and (task.loader.target_regex or self.auto_delayed_regex):
+                    if re.match(task.loader.target_regex if task.loader.target_regex else '.*', filter_):
+                        tasks.append(task)
+            if len(tasks) > 0:
+                if len(tasks) == 1:
+                    task = tasks[0]
+                    loader = task.loader
+                    loader.basename = task.name
+                    name = '_regex_target_' + filter_
+                    self.tasks[name] = Task(name, None,
+                                            loader=loader,
+                                            file_dep=[filter_])
+                    selected_task.append(name)
                 else:
-                    msg = ('cmd `run` invalid parameter: "%s".' +
-                           ' Must be a task, or a target.\n' +
-                           'Type "doit list" to see available tasks')
-                    raise InvalidCommand(msg % filter_)
+                    name = '_regex_target_' + filter_
+                    self.tasks[name] = Task(name, None,
+                                            task_dep=[task.name for task in tasks],
+                                            file_dep=[filter_])
+                    selected_task.append(name)
+            else:
+                # not found
+                msg = ('cmd `run` invalid parameter: "%s".' +
+                       ' Must be a task, or a target.\n' +
+                       'Type "doit list" to see available tasks')
+                raise InvalidCommand(msg % filter_)
         return selected_task
 
 
@@ -416,6 +446,9 @@ class TaskDispatcher(object):
             basename = this_task.loader.basename or this_task.name
             new_tasks = generate_tasks(basename, ref(), ref.__doc__)
             TaskControl.set_implicit_deps(self.targets, new_tasks)
+            # check itself for implicit dep (used by regex_target)
+            TaskControl.add_implicit_task_dep(
+                self.targets, this_task, this_task.file_dep)
             for nt in new_tasks:
                 if not nt.loader:
                     nt.loader = DelayedLoaded
diff --git a/doit/loader.py b/doit/loader.py
index 1f3bcd4..123d0f4 100644
--- a/doit/loader.py
+++ b/doit/loader.py
@@ -98,10 +98,11 @@ def get_module(dodo_file, cwd=None, seek_parent=False):
 
 
 
-def create_after(executed=None):
+def create_after(executed=None, target_regex=None):
     """Annotate a task-creator function with delayed loader info"""
     def decorated(func):
-        func.doit_create_after = DelayedLoader(func, executed=executed)
+        func.doit_create_after = DelayedLoader(func, executed=executed,
+                                               target_regex=target_regex)
         return func
     return decorated
 
diff --git a/doit/reporter.py b/doit/reporter.py
index 4f5cbe3..18df356 100644
--- a/doit/reporter.py
+++ b/doit/reporter.py
@@ -53,7 +53,8 @@ class ConsoleReporter(object):
 
     def skip_uptodate(self, task):
         """skipped up-to-date task"""
-        self.write("-- %s\n" % task.title())
+        if task.actions and (task.name[0] != '_'):
+            self.write("-- %s\n" % task.title())
 
     def skip_ignore(self, task):
         """skipped ignored task"""
diff --git a/doit/task.py b/doit/task.py
index 7595877..3440123 100644
--- a/doit/task.py
+++ b/doit/task.py
@@ -31,9 +31,10 @@ class DelayedLoader(object):
                     the loader call the creator function
     :ivar basename: (str) basename used when creating tasks
     """
-    def __init__(self, creator, executed=None):
+    def __init__(self, creator, executed=None, target_regex=None):
         self.creator = creator
         self.task_dep = executed
+        self.target_regex = target_regex
         self.basename = None
         self.created = False
 

specify target of a DelayedTask on command line
Since not all tasks are created before execution starts, it is required some special handling for target names of targets that have not been created yet.

See discussion on https://github.com/getnikola/nikola/issues/1562#issuecomment-70836094

General idea is that if a target is not found, before raising an error to the user try to load DelayedTasks (as done by command list) to look for the given `target` name.

Some considerations:

 1) as of now a DelayedTask creates an implicit `task_dep` for the task given in it's `executed` param. But this `task_dep` is not preserved when the DelayedTask is re-created. It should not only be preserved but all created tasks should include this `task_dep` because the guarantee that the dependent task was already executed wont exist anymore!

2) if the selected tasks for execution includes know tasks and targets they should be executed **before**, any DelayedTask is loaded to look for an unknown `target`. This will ensure that same command line will work nicely even if it is on its first execution.

<bountysource-plugin>
---
Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/7952756-specify-target-of-a-delayedtask-on-command-line?utm_campaign=plugin&utm_content=tracker%2F1885485&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F1885485&utm_medium=issues&utm_source=github).
</bountysource-plugin>
diff --git a/tests/test_control.py b/tests/test_control.py
index 4237acb..734b449 100644
--- a/tests/test_control.py
+++ b/tests/test_control.py
@@ -111,6 +111,43 @@ class TestTaskControlCmdOptions(object):
         assert control.tasks['taskY:foo'].loader.basename == 'taskY'
         assert control.tasks['taskY:foo'].loader is t2.loader
 
+    def test_filter_delayed_regex_single(self):
+        t1 = Task("taskX", None)
+        t2 = Task("taskY", None, loader=DelayedLoader(lambda: None, target_regex='a.*'))
+        t3 = Task("taskZ", None, loader=DelayedLoader(lambda: None, target_regex='b.*'))
+        t4 = Task("taskW", None, loader=DelayedLoader(lambda: None))
+        control = TaskControl([t1, t2, t3, t4], auto_delayed_regex=False)
+        l = control._filter_tasks(['abc'])
+        assert isinstance(t2.loader, DelayedLoader)
+        assert len(l) == 1
+        assert l[0] == '_regex_target_abc'
+        assert control.tasks[l[0]].file_dep == {'abc'}
+        assert control.tasks[l[0]].loader.basename == 'taskY'
+        assert control.tasks[l[0]].loader is t2.loader
+
+    def test_filter_delayed_regex_multiple(self):
+        t1 = Task("taskX", None)
+        t2 = Task("taskY", None, loader=DelayedLoader(lambda: None, target_regex='a.*'))
+        t3 = Task("taskZ", None, loader=DelayedLoader(lambda: None, target_regex='ab.'))
+        t4 = Task("taskW", None, loader=DelayedLoader(lambda: None))
+        control = TaskControl([t1, t2, t3, t4], auto_delayed_regex=False)
+        l = control._filter_tasks(['abc'])
+        assert len(l) == 1
+        assert l[0] == '_regex_target_abc'
+        assert control.tasks[l[0]].file_dep == {'abc'}
+        assert set(control.tasks[l[0]].task_dep) == {t2.name, t3.name}
+
+    def test_filter_delayed_regex_auto(self):
+        t1 = Task("taskX", None)
+        t2 = Task("taskY", None, loader=DelayedLoader(lambda: None, target_regex='a.*'))
+        t3 = Task("taskZ", None, loader=DelayedLoader(lambda: None))
+        control = TaskControl([t1, t2, t3], auto_delayed_regex=True)
+        l = control._filter_tasks(['abc'])
+        assert len(l) == 1
+        assert l[0] == '_regex_target_abc'
+        assert control.tasks[l[0]].file_dep == {'abc'}
+        assert set(control.tasks[l[0]].task_dep) == {t2.name, t3.name}
+
     # filter a non-existent task raises an error
     def testFilterWrongName(self):
         tc =  TaskControl(TASKS_SAMPLE)

pip install -e .[test]
parse_log_pytest
numpy>=1.10 pytest
pytest
apt-get update apt-get install -y libgeos-dev
3.7
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///croot/attrs_1668696182826/work
certifi @ file:///croot/certifi_1671487769961/work/certifi
-e git+https://github.com/pydoit/doit.git@dcefd2159bca7fa80e236fc3fec91688a39ba7c4#egg=doit
flit_core @ file:///opt/conda/conda-bld/flit-core_1644941570762/work/source/flit_core
importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1648562407465/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
numpy @ file:///opt/conda/conda-bld/numpy_and_numpy_base_1653915516269/work
packaging @ file:///croot/packaging_1671697413597/work
pluggy @ file:///tmp/build/80754af9/pluggy_1648042572264/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pyinotify==0.9.6
pytest==7.1.2
six==1.17.0
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
typing_extensions @ file:///croot/typing_extensions_1669924550328/work
zipp @ file:///croot/zipp_1672387121353/work

name: doit
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=22.1.0=py37h06a4308_0
  - blas=1.0=openblas
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - flit-core=3.6.0=pyhd3eb1b0_0
  - importlib-metadata=4.11.3=py37h06a4308_0
  - importlib_metadata=4.11.3=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgfortran-ng=11.2.0=h00389a5_1
  - libgfortran5=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libopenblas=0.3.21=h043d6bf_0
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - numpy=1.21.5=py37hf838250_3
  - numpy-base=1.21.5=py37h1e6e340_3
  - openssl=1.1.1w=h7f8727e_0
  - packaging=22.0=py37h06a4308_0
  - pip=22.3.1=py37h06a4308_0
  - pluggy=1.0.0=py37h06a4308_1
  - py=1.11.0=pyhd3eb1b0_0
  - pytest=7.1.2=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py37h06a4308_0
  - typing_extensions=4.4.0=py37h06a4308_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.11.0=py37h06a4308_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - pyinotify==0.9.6
      - six==1.17.0
prefix: /opt/conda/envs/doit

tests/test_control.py::TestTaskControlCmdOptions::test_filter_delayed_regex_single tests/test_control.py::TestTaskControlCmdOptions::test_filter_delayed_regex_multiple tests/test_control.py::TestTaskControlCmdOptions::test_filter_delayed_regex_auto
tests/test_control.py::TestTaskControlInit::test_addTask tests/test_control.py::TestTaskControlInit::test_targetDependency tests/test_control.py::TestTaskControlInit::test_addTaskSameName tests/test_control.py::TestTaskControlInit::test_addInvalidTask tests/test_control.py::TestTaskControlInit::test_userErrorTaskDependency tests/test_control.py::TestTaskControlInit::test_userErrorSetupTask tests/test_control.py::TestTaskControlInit::test_sameTarget tests/test_control.py::TestTaskControlInit::test_wild tests/test_control.py::TestTaskControlInit::test_bug770150_task_dependency_from_target tests/test_control.py::TestTaskControlCmdOptions::testFilter tests/test_control.py::TestTaskControlCmdOptions::testProcessSelection tests/test_control.py::TestTaskControlCmdOptions::testProcessAll tests/test_control.py::TestTaskControlCmdOptions::testFilterPattern tests/test_control.py::TestTaskControlCmdOptions::testFilterSubtask tests/test_control.py::TestTaskControlCmdOptions::testFilterTarget tests/test_control.py::TestTaskControlCmdOptions::test_filter_delayed_subtask tests/test_control.py::TestTaskControlCmdOptions::testFilterWrongName tests/test_control.py::TestTaskControlCmdOptions::testFilterEmptyList tests/test_control.py::TestTaskControlCmdOptions::testOptions tests/test_control.py::TestTaskControlCmdOptions::testPosParam tests/test_control.py::TestExecNode::test_repr tests/test_control.py::TestExecNode::test_ready_select__not_waiting tests/test_control.py::TestExecNode::test_parent_status_failure tests/test_control.py::TestExecNode::test_parent_status_ignore tests/test_control.py::TestExecNode::test_step tests/test_control.py::TestDecoratorNoNone::test_filtering tests/test_control.py::TestTaskDispatcher_GenNone::test_create tests/test_control.py::TestTaskDispatcher_GenNone::test_already_created tests/test_control.py::TestTaskDispatcher_GenNone::test_cyclic tests/test_control.py::TestTaskDispatcher_node_add_wait_run::test_wait tests/test_control.py::TestTaskDispatcher_node_add_wait_run::test_none tests/test_control.py::TestTaskDispatcher_node_add_wait_run::test_deps_not_ok tests/test_control.py::TestTaskDispatcher_node_add_wait_run::test_calc_dep_already_executed tests/test_control.py::TestTaskDispatcher_add_task::test_no_deps tests/test_control.py::TestTaskDispatcher_add_task::test_task_deps tests/test_control.py::TestTaskDispatcher_add_task::test_task_deps_already_created tests/test_control.py::TestTaskDispatcher_add_task::test_task_deps_no_wait tests/test_control.py::TestTaskDispatcher_add_task::test_calc_dep tests/test_control.py::TestTaskDispatcher_add_task::test_calc_dep_already_executed tests/test_control.py::TestTaskDispatcher_add_task::test_setup_task__run tests/test_control.py::TestTaskDispatcher_add_task::test_delayed_creation tests/test_control.py::TestTaskDispatcher_add_task::test_delayed_creation_sub_task tests/test_control.py::TestTaskDispatcher_get_next_node::test_none tests/test_control.py::TestTaskDispatcher_get_next_node::test_ready tests/test_control.py::TestTaskDispatcher_get_next_node::test_to_run tests/test_control.py::TestTaskDispatcher_get_next_node::test_to_run_none tests/test_control.py::TestTaskDispatcher_update_waiting::test_wait_select tests/test_control.py::TestTaskDispatcher_update_waiting::test_wait_run tests/test_control.py::TestTaskDispatcher_update_waiting::test_wait_run_deps_not_ok tests/test_control.py::TestTaskDispatcher_update_waiting::test_waiting_node_updated tests/test_control.py::TestTaskDispatcher_dispatcher_generator::test_normal tests/test_control.py::TestTaskDispatcher_dispatcher_generator::test_delayed_creation
doit/loader.py doit/cmd_run.py doit/control.py doit/task.py doit/reporter.py
doit/loader.py doit/cmd_run.py doit/control.py doit/task.py doit/reporter.py