diff --git a/src/nix/develop.cc b/src/nix/develop.cc
index 9a93cdb03a7b906151fefecce6a0fb586ef361b1..55023545d2e499f24ac50411f6f607c486339402 100644
--- a/src/nix/develop.cc
+++ b/src/nix/develop.cc
@@ -7,6 +7,7 @@
 #include "derivations.hh"
 #include "affinity.hh"
 #include "progress-bar.hh"
+#include "run.hh"
 
 #include <nlohmann/json.hpp>
 
@@ -472,8 +473,6 @@ struct CmdDevelop : Common, MixEnvironment
 
         writeFull(rcFileFd.get(), script);
 
-        stopProgressBar();
-
         setEnviron();
         // prevent garbage collection until shell exits
         setenv("NIX_GCROOT", gcroot.data(), 1);
@@ -506,11 +505,7 @@ struct CmdDevelop : Common, MixEnvironment
         auto args = phase || !command.empty() ? Strings{std::string(baseNameOf(shell)), rcFilePath}
             : Strings{std::string(baseNameOf(shell)), "--rcfile", rcFilePath};
 
-        restoreProcessContext();
-
-        execvp(shell.c_str(), stringsToCharPtrs(args).data());
-
-        throw SysError("executing shell '%s'", shell);
+        runProgramInStore(store, shell, args);
     }
 };
 
diff --git a/src/nix/run.cc b/src/nix/run.cc
index 0c8afec2dc535976b6cdf010769a8a96209feda6..7597b61f75089ad4df23342927172439b60540fa 100644
--- a/src/nix/run.cc
+++ b/src/nix/run.cc
@@ -1,3 +1,4 @@
+#include "run.hh"
 #include "command.hh"
 #include "common-args.hh"
 #include "shared.hh"
@@ -20,45 +21,43 @@ using namespace nix;
 
 std::string chrootHelperName = "__run_in_chroot";
 
-struct RunCommon : virtual Command
-{
+namespace nix {
 
-    using Command::run;
+void runProgramInStore(ref<Store> store,
+    const std::string & program,
+    const Strings & args)
+{
+    stopProgressBar();
 
-    void runProgram(ref<Store> store,
-        const std::string & program,
-        const Strings & args)
-    {
-        stopProgressBar();
+    restoreProcessContext();
 
-        restoreProcessContext();
+    /* If this is a diverted store (i.e. its "logical" location
+       (typically /nix/store) differs from its "physical" location
+       (e.g. /home/eelco/nix/store), then run the command in a
+       chroot. For non-root users, this requires running it in new
+       mount and user namespaces. Unfortunately,
+       unshare(CLONE_NEWUSER) doesn't work in a multithreaded program
+       (which "nix" is), so we exec() a single-threaded helper program
+       (chrootHelper() below) to do the work. */
+    auto store2 = store.dynamic_pointer_cast<LocalStore>();
 
-        /* If this is a diverted store (i.e. its "logical" location
-           (typically /nix/store) differs from its "physical" location
-           (e.g. /home/eelco/nix/store), then run the command in a
-           chroot. For non-root users, this requires running it in new
-           mount and user namespaces. Unfortunately,
-           unshare(CLONE_NEWUSER) doesn't work in a multithreaded
-           program (which "nix" is), so we exec() a single-threaded
-           helper program (chrootHelper() below) to do the work. */
-        auto store2 = store.dynamic_pointer_cast<LocalStore>();
+    if (store2 && store->storeDir != store2->getRealStoreDir()) {
+        Strings helperArgs = { chrootHelperName, store->storeDir, store2->getRealStoreDir(), program };
+        for (auto & arg : args) helperArgs.push_back(arg);
 
-        if (store2 && store->storeDir != store2->getRealStoreDir()) {
-            Strings helperArgs = { chrootHelperName, store->storeDir, store2->getRealStoreDir(), program };
-            for (auto & arg : args) helperArgs.push_back(arg);
+        execv(readLink("/proc/self/exe").c_str(), stringsToCharPtrs(helperArgs).data());
 
-            execv(readLink("/proc/self/exe").c_str(), stringsToCharPtrs(helperArgs).data());
+        throw SysError("could not execute chroot helper");
+    }
 
-            throw SysError("could not execute chroot helper");
-        }
+    execvp(program.c_str(), stringsToCharPtrs(args).data());
 
-        execvp(program.c_str(), stringsToCharPtrs(args).data());
+    throw SysError("unable to execute '%s'", program);
+}
 
-        throw SysError("unable to execute '%s'", program);
-    }
-};
+}
 
-struct CmdShell : InstallablesCommand, RunCommon, MixEnvironment
+struct CmdShell : InstallablesCommand, MixEnvironment
 {
 
     using InstallablesCommand::run;
@@ -125,13 +124,13 @@ struct CmdShell : InstallablesCommand, RunCommon, MixEnvironment
         Strings args;
         for (auto & arg : command) args.push_back(arg);
 
-        runProgram(store, *command.begin(), args);
+        runProgramInStore(store, *command.begin(), args);
     }
 };
 
 static auto rCmdShell = registerCommand<CmdShell>("shell");
 
-struct CmdRun : InstallableCommand, RunCommon
+struct CmdRun : InstallableCommand
 {
     using InstallableCommand::run;
 
@@ -183,7 +182,7 @@ struct CmdRun : InstallableCommand, RunCommon
         Strings allArgs{app.program};
         for (auto & i : args) allArgs.push_back(i);
 
-        runProgram(store, app.program, allArgs);
+        runProgramInStore(store, app.program, allArgs);
     }
 };
 
diff --git a/src/nix/run.hh b/src/nix/run.hh
new file mode 100644
index 0000000000000000000000000000000000000000..6180a87dd5a2eb08ef872e643d845508e4d7ecb5
--- /dev/null
+++ b/src/nix/run.hh
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "store-api.hh"
+
+namespace nix {
+
+void runProgramInStore(ref<Store> store,
+    const std::string & program,
+    const Strings & args);
+
+}