diff --git a/functional.hpp b/functional.hpp
index 891230a6ab7f145af80016d0cbaf844c8cb9a85f..8e6087142eeb272c1a58e7c7a1fc34bfa92af98e 100644
--- a/functional.hpp
+++ b/functional.hpp
@@ -9,6 +9,7 @@
 #include <list>
 #include <functional>
 #include <chrono>
+#include <future>
 #include <stdexcept>
 
 namespace rlib {
@@ -44,7 +45,18 @@ namespace rlib {
         auto begin = std::chrono::high_resolution_clock::now();
         f(std::forward<Args>(args) ...);
         auto end = std::chrono::high_resolution_clock::now();
-        return ::std::chrono::duration<double>(end - begin).count(); 
+        return std::chrono::duration<double>(end - begin).count(); 
+    }
+    template <typename Func, typename... Args>
+    static inline auto timeout(int timeout_seconds, Func&& func, Args&&... args) {
+        using ReturnType = decltype(func(args...));
+        auto future = std::async(std::launch::async, std::forward<Func>(func), std::forward<Args>(args)...);
+    
+        if (future.wait_for(std::chrono::seconds(timeout_seconds)) == std::future_status::timeout) {
+            return ReturnType {};
+        }
+    
+        return future.get();
     }
 
     template <class Func, typename... Args>
diff --git a/sys/unix_handy.hpp b/sys/unix_handy.hpp
index c65110bb6fac2704971822aa26f4fce9ab9737c4..faedefd51eaf6fb42d60d2fe6acbbeac2a537648 100644
--- a/sys/unix_handy.hpp
+++ b/sys/unix_handy.hpp
@@ -2,18 +2,18 @@
 #define RLIB_UNIX_HANDY_HPP_
 
 #include <unistd.h>
-
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <netdb.h>
-#include <rlib/scope_guard.hpp>
-#include <rlib/string.hpp>
+#include <string>
+#include <stdexcept>
+#include <vector>
 
 #include <rlib/sys/os.hpp>
 #if RLIB_OS_ID == OS_WINDOWS
 #error rlib/sys/unix_handy.hpp is not for Windows.
 #endif
 
+// For shell_run
+#include <sstream>
+
 namespace rlib {
     // args DOES NOT contain the "$0".
     inline void execs(std::string path, std::vector<std::string> args) {
@@ -28,10 +28,46 @@ namespace rlib {
     
         ::execv(path.c_str(), arr);
     }
+
+    struct shell_result {
+        int status;
+        std::string stdout_;
+    };
+    
+    // Execute command with shell and capture stdout.
+    // Note: stderr would be discarded. Use `2>&1` if needed.
+    shell_result shell_run(const std::string& command) {
+        char buffer[128];
+    
+        FILE *pipe = popen(command.c_str(), "r");
+        if (!pipe) {
+            return {-errno, ""};
+        }
+    
+        shell_result res;
+
+        while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
+            res.stdout_ += buffer;
+        }
+    
+        res.status = pclose(pipe);
+        res.status = WIFEXITED(res.status) ? WEXITSTATUS(res.status) : -errno;
+    
+        return res;
+    }
+
+    auto get_shell_name() {
+        return shell_run("echo -n $0").stdout_;
+    }
 }
 
 // Deprecated. Use sys/sio.hpp
 #if 1+1 == 4
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netdb.h>
+#include <rlib/scope_guard.hpp>
+#include <rlib/string.hpp>
 namespace rlib {
     namespace impl {
         using rlib::literals::operator""_format;