diff --git a/src/lib/builtins/mod.rs b/src/lib/builtins/mod.rs
index 906e3be7f42795094cd86daa57ad495abcf114a5..afd4c1a1295dfdc5c911f6667a0ba59077abe7b9 100644
--- a/src/lib/builtins/mod.rs
+++ b/src/lib/builtins/mod.rs
@@ -38,7 +38,7 @@ use hashbrown::HashMap;
 use liner::{Completer, Context};
 
 use crate::{
-    shell::{status::Status, Capture, Shell},
+    shell::{status::Status, Capture, Shell, Value},
     sys, types,
 };
 use itertools::Itertools;
@@ -191,7 +191,8 @@ impl<'a> BuiltinMap<'a> {
             "popd" => builtin_popd : "Pop a directory from the stack",
             "pushd" => builtin_pushd : "Push a directory to the stack",
             "dirs" => builtin_dirs : "Display the current directory stack",
-            "cd" => builtin_cd : "Change the current directory\n    cd <path>"
+            "cd" => builtin_cd : "Change the current directory\n    cd <path>",
+            "dir_depth" => builtin_dir_depth : "Set the maximum directory depth"
         )
     }
 
@@ -260,12 +261,49 @@ pub fn builtin_status(args: &[small::String], shell: &mut Shell<'_>) -> Status {
     status(args, shell)
 }
 
+pub fn builtin_dir_depth(args: &[small::String], shell: &mut Shell<'_>) -> Status {
+    let depth = match args.get(1) {
+        None => None,
+        Some(arg) => match arg.parse::<usize>() {
+            Ok(num) => Some(num),
+            Err(_) => return Status::error("dir_depth's argument must be a positive integer"),
+        },
+    };
+    shell.dir_stack_mut().set_max_depth(depth);
+    Status::SUCCESS
+}
+
 pub fn builtin_cd(args: &[small::String], shell: &mut Shell<'_>) -> Status {
     if check_help(args, MAN_CD) {
         return Status::SUCCESS;
     }
 
-    match shell.cd(args.get(1)) {
+    let err = match args.get(1) {
+        Some(dir) => {
+            let dir = dir.as_ref();
+            if let Some(Value::Array(cdpath)) = shell.variables().get_ref("CDPATH").cloned() {
+                if dir == "-" {
+                    shell.dir_stack_mut().switch_to_previous_directory()
+                } else {
+                    let check_cdpath_first = cdpath
+                        .iter()
+                        .map(|path| {
+                            let path_dir = format!("{}/{}", path, dir);
+                            shell.dir_stack_mut().change_and_push_dir(&path_dir)
+                        })
+                        .find(Result::is_ok)
+                        .unwrap_or_else(|| shell.dir_stack_mut().change_and_push_dir(dir));
+                    shell.dir_stack_mut().popd(1);
+                    check_cdpath_first
+                }
+            } else {
+                shell.dir_stack_mut().change_and_push_dir(dir)
+            }
+        }
+        None => shell.dir_stack_mut().switch_to_home_directory(),
+    };
+
+    match err {
         Ok(()) => {
             let _ = shell.fork_function(Capture::None, |_| Ok(()), "CD_CHANGE", &["ion"]);
             Status::SUCCESS
@@ -336,7 +374,7 @@ pub fn builtin_dirs(args: &[small::String], shell: &mut Shell<'_>) -> Status {
     }
 
     if clear {
-        shell.clear_dir_stack();
+        shell.dir_stack_mut().clear();
     }
 
     let mapper: fn((usize, &PathBuf)) -> Cow<'_, str> = match (abs_pathnames, index) {
@@ -347,13 +385,13 @@ pub fn builtin_dirs(args: &[small::String], shell: &mut Shell<'_>) -> Status {
         (false, false) => |(_, x)| x.to_string_lossy(),
     };
 
-    let mut iter = shell.dir_stack().enumerate().map(mapper);
+    let mut iter = shell.dir_stack().dirs().enumerate().map(mapper);
 
     if let Some(arg) = num_arg {
         let num = match parse_numeric_arg(arg.as_ref()) {
             Some((true, num)) => num,
-            Some((false, num)) if shell.dir_stack().count() > num => {
-                shell.dir_stack().count() - num - 1
+            Some((false, num)) if shell.dir_stack().dirs().count() > num => {
+                shell.dir_stack().dirs().count() - num - 1
             }
             _ => return Status::error(format!("ion: dirs: {}: invalid argument", arg)),
         };
@@ -404,27 +442,27 @@ pub fn builtin_pushd(args: &[small::String], shell: &mut Shell<'_>) -> Status {
     match action {
         Action::Switch => {
             if !keep_front {
-                if let Err(why) = shell.swap(1) {
+                if let Err(why) = shell.dir_stack_mut().swap(1) {
                     return Status::error(format!("ion: pushd: {}", why));
                 }
             }
         }
         Action::RotLeft(num) => {
             if !keep_front {
-                if let Err(why) = shell.rotate_left(num) {
+                if let Err(why) = shell.dir_stack_mut().rotate_left(num) {
                     return Status::error(format!("ion: pushd: {}", why));
                 }
             }
         }
         Action::RotRight(num) => {
             if !keep_front {
-                if let Err(why) = shell.rotate_right(num) {
+                if let Err(why) = shell.dir_stack_mut().rotate_right(num) {
                     return Status::error(format!("ion: pushd: {}", why));
                 }
             }
         }
         Action::Push(dir) => {
-            if let Err(why) = shell.pushd(dir, keep_front) {
+            if let Err(why) = shell.dir_stack_mut().pushd(dir, keep_front) {
                 return Status::error(format!("ion: pushd: {}", why));
             }
         }
@@ -432,7 +470,11 @@ pub fn builtin_pushd(args: &[small::String], shell: &mut Shell<'_>) -> Status {
 
     println!(
         "{}",
-        shell.dir_stack().map(|dir| dir.to_str().unwrap_or("ion: no directory found")).join(" ")
+        shell
+            .dir_stack()
+            .dirs()
+            .map(|dir| dir.to_str().unwrap_or("ion: no directory found"))
+            .join(" ")
     );
     Status::SUCCESS
 }
@@ -442,7 +484,7 @@ pub fn builtin_popd(args: &[small::String], shell: &mut Shell<'_>) -> Status {
         return Status::SUCCESS;
     }
 
-    let len = shell.dir_stack().len();
+    let len = shell.dir_stack().dirs().len();
     if len <= 1 {
         return Status::error("ion: popd: directory stack empty");
     }
@@ -478,17 +520,18 @@ pub fn builtin_popd(args: &[small::String], shell: &mut Shell<'_>) -> Status {
         index = 1;
     } else if index == 0 {
         // change to new directory, return if not possible
-        if let Err(why) = shell.set_current_dir_by_index(1) {
+        if let Err(why) = shell.dir_stack_mut().set_current_dir_by_index(1) {
             return Status::error(format!("ion: popd: {}", why));
         }
     }
 
     // pop element
-    if shell.popd(index).is_some() {
+    if shell.dir_stack_mut().popd(index).is_some() {
         println!(
             "{}",
             shell
                 .dir_stack()
+                .dirs()
                 .map(|dir| dir.to_str().unwrap_or("ion: no directory found"))
                 .join(" ")
         );
diff --git a/src/lib/shell/directory_stack.rs b/src/lib/shell/directory_stack.rs
index 28e89b47956c2ac33711dc5de412a71c9f36beb5..25bf4a954eb403b79fdabf7da81c757564abdd36 100644
--- a/src/lib/shell/directory_stack.rs
+++ b/src/lib/shell/directory_stack.rs
@@ -1,4 +1,3 @@
-use super::variables::{Value, Variables};
 use crate::sys::env as sys_env;
 use err_derive::Error;
 use std::{
@@ -44,7 +43,8 @@ fn set_current_dir_ion(dir: &Path) -> Result<(), DirStackError> {
 
 #[derive(Debug)]
 pub struct DirectoryStack {
-    dirs: VecDeque<PathBuf>, // The top is always the current directory
+    dirs:      VecDeque<PathBuf>, // The top is always the current directory
+    max_depth: Option<usize>,
 }
 
 impl DirectoryStack {
@@ -72,6 +72,10 @@ impl DirectoryStack {
         new_dir
     }
 
+    pub fn set_max_depth(&mut self, max_depth: Option<usize>) { self.max_depth = max_depth; }
+
+    pub fn max_depth(&mut self) -> Option<usize> { self.max_depth }
+
     // pushd -<num>
     pub fn rotate_right(&mut self, num: usize) -> Result<(), DirStackError> {
         let len = self.dirs.len();
@@ -105,24 +109,24 @@ impl DirectoryStack {
         self.dirs.iter()
     }
 
-    fn insert_dir(&mut self, index: usize, path: PathBuf, variables: &Variables<'_>) {
+    fn insert_dir(&mut self, index: usize, path: PathBuf) {
         self.dirs.insert(index, path);
-        self.dirs.truncate(DirectoryStack::get_size(variables));
+        if let Some(max_depth) = self.max_depth {
+            self.dirs.truncate(max_depth);
+        }
     }
 
-    fn push_dir(&mut self, path: PathBuf, variables: &Variables<'_>) {
+    fn push_dir(&mut self, path: PathBuf) {
         self.dirs.push_front(path);
-        self.dirs.truncate(DirectoryStack::get_size(variables));
+        if let Some(max_depth) = self.max_depth {
+            self.dirs.truncate(max_depth);
+        }
     }
 
-    fn change_and_push_dir(
-        &mut self,
-        dir: &str,
-        variables: &Variables<'_>,
-    ) -> Result<(), DirStackError> {
+    pub fn change_and_push_dir(&mut self, dir: &str) -> Result<(), DirStackError> {
         let new_dir = self.normalize_path(dir);
         set_current_dir_ion(&new_dir)?;
-        self.push_dir(new_dir, variables);
+        self.push_dir(new_dir);
         Ok(())
     }
 
@@ -130,56 +134,22 @@ impl DirectoryStack {
         env::var("OLDPWD").ok().filter(|pwd| !pwd.is_empty() && pwd != "?")
     }
 
-    fn switch_to_previous_directory(
-        &mut self,
-        variables: &Variables<'_>,
-    ) -> Result<(), DirStackError> {
+    pub fn switch_to_previous_directory(&mut self) -> Result<(), DirStackError> {
         let prev = self.get_previous_dir().ok_or(DirStackError::NoPreviousDir)?;
 
         self.dirs.remove(0);
         println!("{}", prev);
-        self.change_and_push_dir(&prev, variables)
+        self.change_and_push_dir(&prev)
     }
 
-    fn switch_to_home_directory(&mut self, variables: &Variables<'_>) -> Result<(), DirStackError> {
+    pub fn switch_to_home_directory(&mut self) -> Result<(), DirStackError> {
         sys_env::home_dir().map_or(Err(DirStackError::FailedFetchHome), |home| {
             home.to_str().map_or(Err(DirStackError::PathConversionFailed), |home| {
-                self.change_and_push_dir(home, variables)
+                self.change_and_push_dir(home)
             })
         })
     }
 
-    pub fn cd<T: AsRef<str>>(
-        &mut self,
-        dir: Option<T>,
-        variables: &Variables<'_>,
-    ) -> Result<(), DirStackError> {
-        match dir {
-            Some(dir) => {
-                let dir = dir.as_ref();
-                if let Some(Value::Array(cdpath)) = variables.get_ref("CDPATH") {
-                    if dir == "-" {
-                        self.switch_to_previous_directory(variables)
-                    } else {
-                        let check_cdpath_first = cdpath
-                            .iter()
-                            .map(|path| {
-                                let path_dir = format!("{}/{}", path, dir);
-                                self.change_and_push_dir(&path_dir, variables)
-                            })
-                            .find(Result::is_ok)
-                            .unwrap_or_else(|| self.change_and_push_dir(dir, variables));
-                        self.dirs.remove(1);
-                        check_cdpath_first
-                    }
-                } else {
-                    self.change_and_push_dir(dir, variables)
-                }
-            }
-            None => self.switch_to_home_directory(variables),
-        }
-    }
-
     pub fn swap(&mut self, index: usize) -> Result<(), DirStackError> {
         if self.dirs.len() <= index {
             return Err(DirStackError::NoOtherDir);
@@ -188,15 +158,10 @@ impl DirectoryStack {
         self.set_current_dir_by_index(0)
     }
 
-    pub fn pushd(
-        &mut self,
-        path: PathBuf,
-        keep_front: bool,
-        variables: &mut Variables<'_>,
-    ) -> Result<(), DirStackError> {
+    pub fn pushd(&mut self, path: PathBuf, keep_front: bool) -> Result<(), DirStackError> {
         let index = if keep_front { 1 } else { 0 };
         let new_dir = self.normalize_path(path.to_str().unwrap());
-        self.insert_dir(index, new_dir, variables);
+        self.insert_dir(index, new_dir);
         self.set_current_dir_by_index(index)
     }
 
@@ -206,19 +171,6 @@ impl DirectoryStack {
 
     pub fn clear(&mut self) { self.dirs.truncate(1) }
 
-    /// This function will take a map of variables as input and attempt to parse the value of
-    /// the
-    /// directory stack size variable. If it succeeds, it will return the value of that
-    /// variable,
-    /// else it will return a default value of 1000.
-    fn get_size(variables: &Variables<'_>) -> usize {
-        variables
-            .get_str("DIRECTORY_STACK_SIZE")
-            .unwrap_or_default()
-            .parse::<usize>()
-            .unwrap_or(1000)
-    }
-
     /// Create a new `DirectoryStack` containing the current working directory,
     /// if available.
     pub fn new() -> DirectoryStack {
@@ -233,6 +185,6 @@ impl DirectoryStack {
                 env::set_var("PWD", "?");
             }
         }
-        DirectoryStack { dirs }
+        DirectoryStack { dirs, max_depth: None }
     }
 }
diff --git a/src/lib/shell/mod.rs b/src/lib/shell/mod.rs
index 6793c1cbe3b204ea344c8ba1d70cdcfae7a49eec..63b8b7b34f9cc8b57164763bb385632617fd57c8 100644
--- a/src/lib/shell/mod.rs
+++ b/src/lib/shell/mod.rs
@@ -14,7 +14,7 @@ pub mod variables;
 
 pub(crate) use self::job::Job;
 use self::{
-    directory_stack::{DirStackError, DirectoryStack},
+    directory_stack::DirectoryStack,
     flow_control::{Block, FunctionError, Statement},
     foreground::ForegroundSignals,
     fork::{Fork, IonResult},
@@ -38,7 +38,7 @@ use std::{
     fs::{self, OpenOptions},
     io::{self, Write},
     ops::{Deref, DerefMut},
-    path::{Path, PathBuf},
+    path::Path,
     process,
     sync::{atomic::Ordering, Arc, Mutex},
     time::SystemTime,
@@ -229,37 +229,9 @@ impl<'a> Shell<'a> {
         }
     }
 
-    pub fn rotate_right(&mut self, num: usize) -> Result<(), DirStackError> {
-        self.directory_stack.rotate_right(num)
-    }
-
-    pub fn rotate_left(&mut self, num: usize) -> Result<(), DirStackError> {
-        self.directory_stack.rotate_left(num)
-    }
-
-    pub fn swap(&mut self, index: usize) -> Result<(), DirStackError> {
-        self.directory_stack.swap(index)
-    }
-
-    pub fn set_current_dir_by_index(&self, index: usize) -> Result<(), DirStackError> {
-        self.directory_stack.set_current_dir_by_index(index)
-    }
-
-    pub fn cd<T: AsRef<str>>(&mut self, dir: Option<T>) -> Result<(), DirStackError> {
-        self.directory_stack.cd(dir, &self.variables)
-    }
-
-    pub fn pushd(&mut self, path: PathBuf, keep_front: bool) -> Result<(), DirStackError> {
-        self.directory_stack.pushd(path, keep_front, &mut self.variables)
-    }
-
-    pub fn popd(&mut self, index: usize) -> Option<PathBuf> { self.directory_stack.popd(index) }
-
-    pub fn dir_stack(&self) -> impl DoubleEndedIterator<Item = &PathBuf> + ExactSizeIterator {
-        self.directory_stack.dirs()
-    }
+    pub fn dir_stack(&self) -> &DirectoryStack { &self.directory_stack }
 
-    pub fn clear_dir_stack(&mut self) { self.directory_stack.clear() }
+    pub fn dir_stack_mut(&mut self) -> &mut DirectoryStack { &mut self.directory_stack }
 
     /// Resets the flow control fields to their default values.
     pub fn reset_flow(&mut self) { self.flow_control.clear(); }