diff --git a/README.md b/README.md
index bef4c03ba6b1cbeac8cd6057a2b482318db872f7..7b382eb96433bce55499e1026e6398575e3815d7 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,7 @@ Ion is a shell for UNIX platforms, and is the default shell in Redox. It is stil
 - [x] Executing Scripts with an @args Array
 - [x] Aliases
 - [x] Variables (**$variable**)
+- [x] Multiple Variable Assignments
 - [x] Substring Slicing of Variables
 - [x] Arrays (**@array**)
 - [x] Array Expressions (**[]**)
@@ -70,6 +71,14 @@ a similar action, only setting the variable globally as an environment variable
 let git_branch = $(git rev-parse --abbrev-ref HEAD ^> /dev/null)
 ```
 
+It is also possible to assign multiple variables at once, or swap variables.
+
+```ion
+let a b = 1 2
+let a b = [1 2]
+let a b = [$b $a]
+```
+
 If the command is executed without any arguments, it will simply list all available variables.
 
 ### Using Variables
diff --git a/examples/fibonacci.ion b/examples/fibonacci.ion
index cf7c877f6e10ca30c46292e3d1ee504630504f18..94cee368a22e6a32b448aa2d14ae94a472ca3c93 100644
--- a/examples/fibonacci.ion
+++ b/examples/fibonacci.ion
@@ -2,8 +2,7 @@ fn fib n
     if test $n -le 1
         echo $n
     else
-        let output = 1
-        let previous = 1
+        let output previous = 1 1
         for _ in 2..$n
             let temp = $output
             let output += $previous
diff --git a/examples/let.ion b/examples/let.ion
new file mode 100644
index 0000000000000000000000000000000000000000..43faa47a82afdc352c0aeb52219b5ca97bd4ad67
--- /dev/null
+++ b/examples/let.ion
@@ -0,0 +1,12 @@
+let a = 5
+echo $a
+let a b = [10 $a]
+echo $a
+echo $b
+let b a = [$a $b]
+echo $a
+echo $b
+let a b c = 1 2 3
+echo $a
+echo $b
+echo $c
diff --git a/examples/let.out b/examples/let.out
new file mode 100644
index 0000000000000000000000000000000000000000..5f0f24c642d49dc437df4f4f603e4fe7265c9362
--- /dev/null
+++ b/examples/let.out
@@ -0,0 +1,8 @@
+5
+10
+5
+5
+10
+1
+2
+3
diff --git a/src/parser/assignments.rs b/src/parser/assignments.rs
index e3504289514e07baf50f895df269eb8d5c2e94b1..aea3db8952e4824b64b06d9bae02f45a8f5aba51 100644
--- a/src/parser/assignments.rs
+++ b/src/parser/assignments.rs
@@ -15,6 +15,7 @@ pub enum Binding {
     KeyOnly(Identifier),
     KeyValue(Identifier, VString),
     Math(Identifier, Operator, VString),
+    MultipleKeys(Vec<Identifier>, VString)
 }
 
 #[derive(Debug, PartialEq, Clone)]
@@ -39,6 +40,7 @@ pub fn parse_assignment(arguments: &str) -> Binding {
 
     // Find the key and advance the iterator until the equals operator is found.
     let mut key = "".to_owned();
+    let mut keys: Vec<Identifier> = Vec::new();
     let mut found_key = false;
     let mut operator = None;
 
@@ -56,7 +58,10 @@ pub fn parse_assignment(arguments: &str) -> Binding {
     while let Some(character) = char_iter.next() {
         match character {
             ' ' if key.is_empty() => (),
-            ' ' => found_key = true,
+            ' ' => {
+                keys.push(key.clone().into());
+                key.clear();
+            },
             '+' => {
                 match_operator!(Operator::Add);
                 break
@@ -78,6 +83,7 @@ pub fn parse_assignment(arguments: &str) -> Binding {
                 break
             },
             '=' => {
+                if !key.is_empty() { keys.push(key.into()); }
                 found_key = true;
                 break
             },
@@ -86,9 +92,19 @@ pub fn parse_assignment(arguments: &str) -> Binding {
         }
     }
 
-    if !found_key && key.is_empty() {
+    if !found_key {
+        Binding::ListEntries
+    } else if keys.len() > 1 {
+        for key in &keys {
+            if !Variables::is_valid_variable_name(&key) {
+                return Binding::InvalidKey(key.clone());
+            }
+        }
+        Binding::MultipleKeys(keys, char_iter.skip_while(|&x| x == ' ').collect::<VString>())
+    } else if keys.is_empty() {
         Binding::ListEntries
     } else {
+        let key = keys.drain(..).next().unwrap();
         let value = char_iter.skip_while(|&x| x == ' ').collect::<VString>();
         if value.is_empty() {
             Binding::KeyOnly(key.into())
diff --git a/src/shell/assignments.rs b/src/shell/assignments.rs
index c3ce46e388ca90c807195b8b5fa1ef4328944281..79da421c7229f5ae70e980035a7df403313c4318 100644
--- a/src/shell/assignments.rs
+++ b/src/shell/assignments.rs
@@ -23,6 +23,7 @@ use super::status::*;
 
 enum Action {
     UpdateString(Identifier, VString),
+    UpdateStrings(Vec<Identifier>, VArray),
     UpdateArray(Identifier, VArray),
     List
 }
@@ -77,6 +78,14 @@ fn parse_assignment(binding: Binding,
             Value::String(value) => Ok(Action::UpdateString(key, value)),
             Value::Array(array)  => Ok(Action::UpdateArray(key, array))
         },
+        Binding::MultipleKeys(keys, value) => match parse_expression(&value, &expanders) {
+            Value::String(value) => {
+                let array = value.split_whitespace().map(String::from)
+                    .collect::<VArray>();
+                Ok(Action::UpdateStrings(keys, array))
+            },
+            Value::Array(array)  => Ok(Action::UpdateStrings(keys, array))
+        },
         Binding::KeyOnly(key) => {
             let stderr = io::stderr();
             let _ = writeln!(&mut stderr.lock(), "ion: please provide value for variable '{}'", key);
@@ -120,6 +129,11 @@ pub fn let_assignment(binding: Binding, vars: &mut Variables, dir_stack: &Direct
     match parse_assignment(binding, vars, dir_stack) {
         Ok(Action::UpdateArray(key, array)) => vars.set_array(&key, array),
         Ok(Action::UpdateString(key, string)) => vars.set_var(&key, &string),
+        Ok(Action::UpdateStrings(keys, array)) => {
+            for (key, value) in keys.iter().zip(array.iter()) {
+                vars.set_var(key, value);
+            }
+        },
         Ok(Action::List) => {
             print_vars(&vars.variables);
             print_arrays(&vars.arrays);
@@ -136,6 +150,11 @@ pub fn export_variable(binding : Binding, vars: &mut Variables, dir_stack : &Dir
     match parse_assignment(binding, vars, dir_stack) {
         Ok(Action::UpdateArray(key, array)) => env::set_var(&key, array.join(" ")),
         Ok(Action::UpdateString(key, string)) => env::set_var(&key, string),
+        Ok(Action::UpdateStrings(keys, array)) => {
+            for (key, value) in keys.iter().zip(array.iter()) {
+                env::set_var(key, value);
+            }
+        }
         Ok(Action::List) => {
             let stdout = io::stdout();
             let stdout = &mut stdout.lock();