diff --git a/Cargo.lock b/Cargo.lock
index dee94e86ddca21caf9ed253b262a99f22d83c38e..3653001e19bb9d22949a028d28a412719d5915bc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -340,6 +340,14 @@ dependencies = [
  "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "heck"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "ion-ranges"
 version = "0.1.0"
@@ -375,6 +383,7 @@ dependencies = [
  "serial_test_derive 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "small 0.1.0 (git+https://gitlab.redox-os.org/redox-os/small)",
  "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "structopt 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -943,6 +952,26 @@ name = "strsim"
 version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "structopt"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "structopt-derive 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "structopt-derive"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "syn"
 version = "0.15.34"
@@ -1137,6 +1166,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
 "checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
 "checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da"
+"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
 "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
 "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
 "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
@@ -1201,6 +1231,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum stackvector 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c049c77bf85fbc036484c97b008276d539d9ebff9dfbde37b632ebcd5b8746b6"
 "checksum static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5"
 "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+"checksum structopt 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "c767a8971f53d7324583085deee2e230903be09e52fb27df9af94c5cb2b43c31"
+"checksum structopt-derive 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "c57a30c87454ced2186f62f940e981746e8cbbe026d52090c8c4352b636f8235"
 "checksum syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)" = "a1393e4a97a19c01e900df2aec855a29f71cf02c402e2f443b8d2747c25c5dbe"
 "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f"
 "checksum termion 1.5.2 (git+https://gitlab.redox-os.org/redox-os/termion)" = "<none>"
diff --git a/Cargo.toml b/Cargo.toml
index b044334c693c18d7a194eb756c77241a530d5376..b182580ee998a1b6a46a22816f7e5e407c4d232b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,6 +20,9 @@ repository = "https://gitlab.redox-os.org/redox-os/ion"
 version = "1.0.0-alpha"
 edition = "2018"
 
+[features]
+advanced_arg_parsing = []
+
 [workspace]
 members = [
     "members/braces", "members/builtins", "members/lexers", "members/sys",
@@ -74,6 +77,7 @@ itertools = "0.8"
 lexical = "2.0"
 object-pool = "0.3.1"
 auto_enums = "0.5.5"
+structopt = "^0.2"
 
 [lib]
 path = "src/lib/lib.rs"
diff --git a/examples/help.out b/examples/help.out
index 247edcde7998134a66cecc3fe91c12410943fc38..0f702d30d236d85bac966c29ea8f73b653475215 100644
--- a/examples/help.out
+++ b/examples/help.out
@@ -1,22 +1,21 @@
-NAME
-    Ion - The Ion shell
+Ion - The Ion Shell 1.0.0-alpha
+Ion is a commandline shell created to be a faster and easier to use alternative to the currently available shells. It is
+not POSIX compliant.
 
-SYNOPSIS
-    ion [options] [args...]
+USAGE:
+    ion [FLAGS] [OPTIONS] [args]...
 
-DESCRIPTION
-    Ion is a commandline shell created to be a faster and easier to use alternative to the
-    currently available shells. It is not POSIX compliant.
+FLAGS:
+    -h, --help           Prints help information
+    -i, --interactive    Force interactive mode
+    -n, --no-execute     Do not execute any commands, perform only syntax checking
+    -x                   Print commands before execution
+    -v, --version        Print the version, platform and revision of Ion then exit
 
 OPTIONS:
-    -c <command>        evaluates given commands instead of reading from the commandline.
-
-    -n or --no-execute
-        do not execute any commands, just do syntax checking.
-
-    -v or --version
-        prints the version, platform and revision of ion then exits.
+    -c <command>             Evaluate given commands instead of reading from the commandline
+    -o <key_bindings>        Shortcut layout. Valid options: "vi", "emacs"
 
 ARGS:
-    <args>...    Script arguments (@args). If the -c option is not specified, the first
-                 parameter is taken as a filename to execute
+    <args>...    Script arguments (@args). If the -c option is not specified, the first parameter is taken as a
+                 filename to execute
diff --git a/examples/run_examples.sh b/examples/run_examples.sh
index c7ca529393082af7a11cd45a0f7f383d23aa43a7..17f85eac0e34d179e7b792e5b36291f54f3c18e0 100755
--- a/examples/run_examples.sh
+++ b/examples/run_examples.sh
@@ -76,24 +76,32 @@ function check_return_value {
     test $1 $1 1
 }
 
+function perform_testing {
+    set +e
+    # Iterate over every Ion script in examples directory
+    for i in $EXAMPLES_DIR/*.ion; do
+        check_return_value $i;
+        if [[ $? -ne 0 ]]; then
+            EXIT_VAL=1;
+        fi
+    done
+
+    # Iterate over every parameter set
+    for i in $EXAMPLES_DIR/*.params; do
+        test_cli $i;
+        if [[ $? -ne 0 ]]; then
+            EXIT_VAL=1;
+        fi
+    done
+}
+
 # Build debug binary
 cargo +$TOOLCHAIN build
+perform_testing
 
-set +e
-# Iterate over every Ion script in examples directory
-for i in $EXAMPLES_DIR/*.ion; do
-    check_return_value $i;
-    if [[ $? -ne 0 ]]; then
-        EXIT_VAL=1;
-    fi
-done
-
-# Iterate over every parameter set
-for i in $EXAMPLES_DIR/*.params; do
-    test_cli $i;
-    if [[ $? -ne 0 ]]; then
-        EXIT_VAL=1;
-    fi
-done
+set -e
+# Build debug binary for testing structopt argument parsing
+cargo +$TOOLCHAIN build --features=advanced_arg_parsing
+perform_testing
 
 exit $EXIT_VAL
diff --git a/src/binary/mod.rs b/src/binary/mod.rs
index 14f7b8a4c9324bdb02fa6da17a818ee0901328cd..b9eb36c7279938dcadf232b2258d15a221be5db3 100644
--- a/src/binary/mod.rs
+++ b/src/binary/mod.rs
@@ -18,28 +18,28 @@ use liner::{Buffer, Context, KeyBindings};
 use std::{cell::RefCell, fs::OpenOptions, io, path::Path, rc::Rc};
 use xdg::BaseDirectories;
 
-pub const MAN_ION: &str = "NAME
-    Ion - The Ion shell
+#[cfg(not(feature = "advanced_arg_parsing"))]
+pub const MAN_ION: &str = r#"Ion - The Ion Shell 1.0.0-alpha
+Ion is a commandline shell created to be a faster and easier to use alternative to the currently available shells. It is
+not POSIX compliant.
 
-SYNOPSIS
-    ion [options] [args...]
+USAGE:
+    ion [FLAGS] [OPTIONS] [args]...
 
-DESCRIPTION
-    Ion is a commandline shell created to be a faster and easier to use alternative to the
-    currently available shells. It is not POSIX compliant.
+FLAGS:
+    -h, --help           Prints help information
+    -i, --interactive    Force interactive mode
+    -n, --no-execute     Do not execute any commands, perform only syntax checking
+    -x                   Print commands before execution
+    -v, --version        Print the version, platform and revision of Ion then exit
 
 OPTIONS:
-    -c <command>        evaluates given commands instead of reading from the commandline.
-
-    -n or --no-execute
-        do not execute any commands, just do syntax checking.
-
-    -v or --version
-        prints the version, platform and revision of ion then exits.
+    -c <command>             Evaluate given commands instead of reading from the commandline
+    -o <key_bindings>        Shortcut layout. Valid options: "vi", "emacs"
 
 ARGS:
-    <args>...    Script arguments (@args). If the -c option is not specified, the first
-                 parameter is taken as a filename to execute";
+    <args>...    Script arguments (@args). If the -c option is not specified, the first parameter is taken as a
+                 filename to execute"#;
 
 pub(crate) const MAN_HISTORY: &str = r#"NAME
     history - print command history
diff --git a/src/main.rs b/src/main.rs
index ff62e8e66905820b80f83c23599fee6435186ec4..9da3f1c2cb0a2fb2082118f6afc2a3f45c37d380 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,98 +1,180 @@
-mod binary;
-
-use self::binary::{builtins, InteractiveBinary, MAN_ION};
+use self::binary::{builtins, InteractiveBinary};
 use ion_shell::{BuiltinMap, IonError, PipelineError, Shell, Value};
 use ion_sys as sys;
 use liner::KeyBindings;
 use std::{
     alloc::System,
-    env,
     io::{self, stdin, BufReader},
     process,
 };
 
+#[cfg(not(feature = "advanced_arg_parsing"))]
+use crate::binary::MAN_ION;
+#[cfg(not(feature = "advanced_arg_parsing"))]
+use std::env;
+#[cfg(feature = "advanced_arg_parsing")]
+use std::str::FromStr;
+#[cfg(feature = "advanced_arg_parsing")]
+use structopt::StructOpt;
+
+mod binary;
+
 #[global_allocator]
 static A: System = System;
 
+struct KeyBindingsWrapper(KeyBindings);
+
+#[cfg(feature = "advanced_arg_parsing")]
+impl FromStr for KeyBindingsWrapper {
+    type Err = String;
+
+    fn from_str(input: &str) -> Result<Self, Self::Err> {
+        match input {
+            "vi" => Ok(KeyBindingsWrapper(KeyBindings::Vi)),
+            "emacs" => Ok(KeyBindingsWrapper(KeyBindings::Emacs)),
+            _ => Err("unknown key bindings".to_string()),
+        }
+    }
+}
+
 fn set_unique_pid() -> io::Result<()> {
     let pid = sys::getpid()?;
     sys::setpgid(0, pid)?;
     sys::tcsetpgrp(0, pid)
 }
 
-fn main() {
-    let mut builtins = BuiltinMap::default().with_shell_unsafe();
-    builtins.add("exec", &builtins::exec, "Replace the shell with the given command.");
-    builtins.add("exit", &builtins::exit, "Exits the current session");
+/// Ion is a commandline shell created to be a faster and easier to use alternative to the
+/// currently available shells. It is not POSIX compliant.
+#[cfg_attr(feature = "advanced_arg_parsing", derive(StructOpt))]
+#[cfg_attr(
+    feature = "advanced_arg_parsing",
+    structopt(
+        name = "Ion - The Ion Shell",
+        author = "",
+        raw(setting = "structopt::clap::AppSettings::ColoredHelp")
+    )
+)]
+struct CommandLineArgs {
+    /// Shortcut layout. Valid options: "vi", "emacs"
+    #[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-o"))]
+    key_bindings: Option<KeyBindingsWrapper>,
+    /// Print commands before execution
+    #[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-x"))]
+    print_commands: bool,
+    /// Force interactive mode
+    #[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-i", long = "--interactive"))]
+    interactive: bool,
+    /// Do not execute any commands, perform only syntax checking
+    #[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-n", long = "--no-execute"))]
+    no_execute: bool,
+    /// Evaluate given commands instead of reading from the commandline
+    #[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-c"))]
+    command: Option<String>,
+    /// Print the version, platform and revision of Ion then exit
+    #[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-v", long = "--version"))]
+    version: bool,
+    /// Script arguments (@args). If the -c option is not specified,
+    /// the first parameter is taken as a filename to execute
+    #[cfg_attr(feature = "advanced_arg_parsing", structopt())]
+    args: Vec<String>,
+}
 
-    let stdin_is_a_tty = sys::isatty(sys::STDIN_FILENO);
-    let mut shell = Shell::with_builtins(builtins, false);
+fn version() -> String { include!(concat!(env!("OUT_DIR"), "/version_string")).to_string() }
 
-    if stdin_is_a_tty {
-        if let Err(why) = set_unique_pid() {
-            eprintln!("ion: could not assign a pid to the shell: {}", why);
-        }
-    }
+#[cfg(feature = "advanced_arg_parsing")]
+fn parse_args() -> CommandLineArgs { CommandLineArgs::from_args() }
 
-    let mut command = None;
+#[cfg(not(feature = "advanced_arg_parsing"))]
+fn parse_args() -> CommandLineArgs {
     let mut args = env::args().skip(1);
-    let mut script_path = None;
+    let mut command = None;
     let mut key_bindings = None;
-    let mut force_interactive = false;
+    let mut no_execute = false;
+    let mut print_commands = false;
+    let mut interactive = false;
+    let mut version = false;
+    let mut additional_arguments = Vec::new();
     while let Some(arg) = args.next() {
         match arg.as_str() {
-            "-o" => match args.next().as_ref().map(|s| s.as_str()) {
-                Some("vi") => key_bindings = Some(KeyBindings::Vi),
-                Some("emacs") => key_bindings = Some(KeyBindings::Emacs),
-                Some(_) => {
-                    eprintln!("ion: invalid option for option -o");
-                    process::exit(1);
+            "-o" => {
+                key_bindings = match args.next().as_ref().map(|s| s.as_str()) {
+                    Some("vi") => Some(KeyBindingsWrapper(KeyBindings::Vi)),
+                    Some("emacs") => Some(KeyBindingsWrapper(KeyBindings::Emacs)),
+                    Some(_) => {
+                        eprintln!("ion: invalid option for option -o");
+                        process::exit(1);
+                    }
+                    None => {
+                        eprintln!("ion: no option given for option -o");
+                        process::exit(1);
+                    }
                 }
-                None => {
-                    eprintln!("ion: no option given for option -o");
-                    process::exit(1);
-                }
-            },
-            "-x" => shell.opts_mut().print_comms = true,
-            "-n" | "--no-execute" => shell.opts_mut().no_exec = true,
-            "-c" => command = args.next(),
-            "-v" | "--version" => {
-                println!(include!(concat!(env!("OUT_DIR"), "/version_string")));
-                return;
             }
+            "-x" => print_commands = true,
+            "-n" | "--no-execute" => no_execute = true,
+            "-c" => command = args.next(),
+            "-v" | "--version" => version = true,
             "-h" | "--help" => {
                 println!("{}", MAN_ION);
-                return;
+                process::exit(0);
             }
-            "-i" | "--interactive" => force_interactive = true,
+            "-i" | "--interactive" => interactive = true,
             _ => {
-                script_path = Some(arg);
-                break;
+                additional_arguments.push(arg);
             }
         }
     }
+    CommandLineArgs {
+        key_bindings,
+        print_commands,
+        interactive,
+        no_execute,
+        command,
+        version,
+        args: additional_arguments,
+    }
+}
+
+fn main() {
+    let command_line_args = parse_args();
+
+    if command_line_args.version {
+        println!("{}", version());
+        return;
+    }
+
+    let mut builtins = BuiltinMap::default().with_shell_unsafe();
+    builtins.add("exec", &builtins::exec, "Replace the shell with the given command.");
+    builtins.add("exit", &builtins::exit, "Exits the current session");
+
+    let stdin_is_a_tty = sys::isatty(sys::STDIN_FILENO);
+    let mut shell = Shell::with_builtins(builtins, false);
+
+    if stdin_is_a_tty {
+        if let Err(why) = set_unique_pid() {
+            eprintln!("ion: could not assign a pid to the shell: {}", why);
+        }
+    }
+
+    shell.opts_mut().print_comms = command_line_args.print_commands;
+    shell.opts_mut().no_exec = command_line_args.no_execute;
 
+    let script_path = command_line_args.args.get(0).cloned();
     shell.variables_mut().set(
         "args",
         Value::Array(
-            script_path
-                .clone()
-                .or_else(|| env::args().next())
-                .into_iter()
-                .chain(args)
-                .map(|arg| Value::Str(arg.into()))
-                .collect(),
+            command_line_args.args.into_iter().map(|arg| Value::Str(arg.into())).collect(),
         ),
     );
 
-    let err = if let Some(command) = command {
+    let err = if let Some(command) = command_line_args.command {
         shell.execute_command(command.as_bytes())
     } else if let Some(path) = script_path {
-        shell.execute_file(&path.as_str())
-    } else if stdin_is_a_tty || force_interactive {
+        shell.execute_file(path)
+    } else if stdin_is_a_tty || command_line_args.interactive {
         let mut interactive = InteractiveBinary::new(shell);
-        if let Some(key_bindings) = key_bindings {
-            interactive.set_keybindings(key_bindings);
+        if let Some(key_bindings) = command_line_args.key_bindings {
+            interactive.set_keybindings(key_bindings.0);
         }
         interactive.add_callbacks();
         interactive.execute_interactive();