diff --git a/src/binary/mod.rs b/src/binary/mod.rs
index 540c4b56fdee27568ccb525b76110ef0a535859f..07de02f14c48042d386991c8bc9796b8ea291d53 100644
--- a/src/binary/mod.rs
+++ b/src/binary/mod.rs
@@ -304,7 +304,7 @@ impl<'a> InteractiveShell<'a> {
                     self.terminated.set(true);
                     {
                         let mut shell = self.shell.borrow_mut();
-                        match shell.on_command(&cmd) {
+                        match shell.on_command(&cmd, true) {
                             Ok(_) => (),
                             Err(IonError::PipelineExecutionError(
                                 PipelineError::CommandNotFound(command),
diff --git a/src/binary/prompt.rs b/src/binary/prompt.rs
index 31e3bf2b7eae96cdb7a94773a4731c6e4e13de09..e00df1f5c0bc45f7f6343e5c289d6b92c28a02cf 100644
--- a/src/binary/prompt.rs
+++ b/src/binary/prompt.rs
@@ -13,30 +13,31 @@ impl<'a> InteractiveShell<'a> {
         let blocks = if self.terminated.get() { shell.block_len() } else { shell.block_len() + 1 };
 
         if blocks == 0 {
-            let out = shell.command("PROMPT").map(|res| res.to_string()).unwrap_or_else(|err| {
-                if let expansion::Error::Subprocess(err) = err {
-                    if let IonError::PipelineExecutionError(PipelineError::CommandNotFound(_)) =
-                        *err
-                    {
-                        match shell
-                            .variables()
-                            .get_str("PROMPT")
-                            .and_then(|prompt| shell.get_string(&prompt))
+            let out =
+                shell.command("PROMPT", false).map(|res| res.to_string()).unwrap_or_else(|err| {
+                    if let expansion::Error::Subprocess(err) = err {
+                        if let IonError::PipelineExecutionError(PipelineError::CommandNotFound(_)) =
+                            *err
                         {
-                            Ok(prompt) => prompt.to_string(),
-                            Err(err) => {
-                                eprintln!("ion: prompt expansion failed: {}", err);
-                                ">>> ".into()
+                            match shell
+                                .variables()
+                                .get_str("PROMPT")
+                                .and_then(|prompt| shell.get_string(&prompt))
+                            {
+                                Ok(prompt) => prompt.to_string(),
+                                Err(err) => {
+                                    eprintln!("ion: prompt expansion failed: {}", err);
+                                    ">>> ".into()
+                                }
                             }
+                        } else {
+                            eprintln!("ion: prompt expansion failed: {}", err);
+                            ">>> ".into()
                         }
                     } else {
-                        eprintln!("ion: prompt expansion failed: {}", err);
-                        ">>> ".into()
+                        panic!("Only a subprocess error should happen inside the pipeline");
                     }
-                } else {
-                    panic!("Only a subprocess error should happen inside the pipeline");
-                }
-            });
+                });
             shell.set_previous_status(previous_status); // Set the previous exit code again
             let key_bindings = self.context.borrow().key_bindings;
             match key_bindings {
diff --git a/src/lib/expansion/mod.rs b/src/lib/expansion/mod.rs
index 0d079570598809f00a60ad256ce9f4ff0bc048b3..9819776d2e93fce260937c8b40e338e6a7cb8389 100644
--- a/src/lib/expansion/mod.rs
+++ b/src/lib/expansion/mod.rs
@@ -127,7 +127,11 @@ pub trait Expander: Sized {
     /// Expand a string variable given if it's quoted / unquoted
     fn string(&self, _name: &str) -> Result<types::Str, Self::Error>;
     /// Expand a subshell expression.
-    fn command(&mut self, _command: &str) -> Result<types::Str, Self::Error>;
+    fn command(
+        &mut self,
+        _command: &str,
+        _set_cmd_duration: bool,
+    ) -> Result<types::Str, Self::Error>;
     /// Iterating upon key-value maps.
     fn map_keys(&self, _name: &str) -> Result<Args, Self::Error>;
     /// Iterating upon key-value maps.
@@ -171,7 +175,7 @@ trait ExpanderInternal: Expander {
         command: &str,
         selection: &Option<&'a str>,
     ) -> Result<(), Self::Error> {
-        let result = self.command(command)?;
+        let result = self.command(command, true)?;
         self.slice(current, result.trim_end_matches('\n'), selection)
     }
 
@@ -703,7 +707,13 @@ pub(crate) mod test {
             }
         }
 
-        fn command(&mut self, cmd: &str) -> Result<types::Str, Self::Error> { Ok(cmd.into()) }
+        fn command(
+            &mut self,
+            cmd: &str,
+            _set_cmd_duration: bool,
+        ) -> Result<types::Str, Self::Error> {
+            Ok(cmd.into())
+        }
 
         fn tilde(&self, input: &str) -> Result<types::Str, Self::Error> { Ok(input.into()) }
 
diff --git a/src/lib/shell/flow.rs b/src/lib/shell/flow.rs
index e142f8490ec9617784b5787e6c2830a496faf694..1ba50333fdedd5ae4b0c1707e76b7dbec10dc85c 100644
--- a/src/lib/shell/flow.rs
+++ b/src/lib/shell/flow.rs
@@ -17,7 +17,7 @@ use crate::{
 use err_derive::Error;
 use itertools::Itertools;
 use nix::unistd::Pid;
-use std::rc::Rc;
+use std::{rc::Rc, time::SystemTime};
 
 #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
 pub enum Condition {
@@ -531,7 +531,7 @@ impl<'a> Shell<'a> {
                 });
 
                 if let Some(statement) = case.conditional.as_ref() {
-                    self.on_command(statement)?;
+                    self.on_command(statement, true)?;
                     if self.previous_status.is_failure() {
                         continue;
                     }
@@ -558,7 +558,13 @@ impl<'a> Shell<'a> {
     }
 
     /// Receives a command and attempts to execute the contents.
-    pub fn on_command(&mut self, command_string: &str) -> std::result::Result<(), IonError> {
+    pub fn on_command(
+        &mut self,
+        command_string: &str,
+        set_cmd_duration: bool,
+    ) -> std::result::Result<(), IonError> {
+        let command_start_time = if set_cmd_duration { Some(SystemTime::now()) } else { None };
+
         for stmt in command_string.bytes().batching(|cmd| Terminator::new(cmd).terminate()) {
             // Go through all of the statements and build up the block stack
             // When block is done return statement for execution.
@@ -569,6 +575,13 @@ impl<'a> Shell<'a> {
                 }
             }
         }
+
+        if let Some(start_time) = command_start_time {
+            if let Ok(elapsed_time) = start_time.elapsed() {
+                self.variables_mut().set("CMD_DURATION", elapsed_time.as_secs().to_string());
+            }
+        }
+
         Ok(())
     }
 }
diff --git a/src/lib/shell/mod.rs b/src/lib/shell/mod.rs
index 6679d51a225354ba1104e43ba5d03628db9c1725..4ec001379bf816e4cdef05d0cce1db33fb1201b0 100644
--- a/src/lib/shell/mod.rs
+++ b/src/lib/shell/mod.rs
@@ -271,7 +271,7 @@ impl<'a> Shell<'a> {
             .filter_map(Result::ok)
             .batching(|bytes| Terminator::new(bytes).terminate())
         {
-            self.on_command(&cmd)?;
+            self.on_command(&cmd, true)?;
         }
 
         if let Some(block) = self.flow_control.last().map(Statement::to_string) {
diff --git a/src/lib/shell/shell_expand.rs b/src/lib/shell/shell_expand.rs
index 0562fef78d296a666c4d00554065677d7f1b06ee..283ba715152466e56dcb81ff934d015ab43fe313 100644
--- a/src/lib/shell/shell_expand.rs
+++ b/src/lib/shell/shell_expand.rs
@@ -16,7 +16,11 @@ impl<'a, 'b> Expander for Shell<'b> {
     type Error = IonError;
 
     /// Uses a subshell to expand a given command.
-    fn command(&mut self, command: &str) -> Result<types::Str, Self::Error> {
+    fn command(
+        &mut self,
+        command: &str,
+        set_cmd_duration: bool,
+    ) -> Result<types::Str, Self::Error> {
         let (mut reader, writer) = create_pipe()
             .map_err(|err| Error::Subprocess(Box::new(IonError::PipelineExecutionError(err))))?;
         let null_file = File::open(NULL_PATH).map_err(|err| {
@@ -30,7 +34,9 @@ impl<'a, 'b> Expander for Shell<'b> {
         let prev_stderr = self.stderr(null_file);
 
         // Execute the command
-        let result = self.on_command(command).map_err(|err| Error::Subprocess(Box::new(err)));
+        let result = self
+            .on_command(command, set_cmd_duration)
+            .map_err(|err| Error::Subprocess(Box::new(err)));
 
         // Reset the pipes, droping the stdout
         self.stdout(prev_stdout);
diff --git a/src/lib/shell/variables.rs b/src/lib/shell/variables.rs
index cf363aa61f4900c8d66afcd692d2e0bea1c4a73e..efcef5ab7262b094249f6a30e51ee197e344edcf 100644
--- a/src/lib/shell/variables.rs
+++ b/src/lib/shell/variables.rs
@@ -286,7 +286,13 @@ pub(crate) mod tests {
             Err(expansion::Error::VarNotFound)
         }
 
-        fn command(&mut self, cmd: &str) -> Result<types::Str, Self::Error> { Ok(cmd.into()) }
+        fn command(
+            &mut self,
+            cmd: &str,
+            _set_cmd_duration: bool,
+        ) -> Result<types::Str, Self::Error> {
+            Ok(cmd.into())
+        }
 
         fn tilde(&self, input: &str) -> Result<types::Str, Self::Error> { Ok(input.into()) }