diff --git a/src/args.rs b/src/args.rs
index 521dda84..cb858f80 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -1,6 +1,7 @@
 use std::cmp;
 use std::env;
-use std::io;
+use std::fs;
+use std::io::{self, BufRead};
 use std::path::{Path, PathBuf};
 use std::process;
 
@@ -34,6 +35,7 @@ use Result;
 /// (TL;DR: The CLI parser is generated from the usage string below.)
 const USAGE: &'static str = "
 Usage: rg [options] -e PATTERN ... [<path> ...]
+       rg [options] -f FILE [<path> ...]
        rg [options] <pattern> [<path> ...]
        rg [options] --files [<path> ...]
        rg [options] --type-list
@@ -107,6 +109,11 @@ Less common options:
     --debug
         Show debug messages.
 
+    -f, --file FILE
+        Search for patterns specified in a file, one per line.  Empty pattern
+        lines will match all input lines, and the newline is not counted as part
+        of the pattern.
+
     --files
         Print each file that would be searched (but don't search).
 
@@ -242,6 +249,7 @@ pub struct RawArgs {
     flag_count: bool,
     flag_files_with_matches: bool,
     flag_debug: bool,
+    flag_file: Option<String>,
     flag_files: bool,
     flag_follow: bool,
     flag_glob: Vec<String>,
@@ -479,23 +487,32 @@ impl RawArgs {
         btypes.build().map_err(From::from)
     }
 
-    fn pattern(&self) -> String {
-        if !self.flag_regexp.is_empty() {
-            if self.flag_fixed_strings {
-                self.flag_regexp.iter().cloned().map(|lit| {
-                    self.word_pattern(regex::quote(&lit))
-                }).collect::<Vec<String>>().join("|")
+    fn pattern(&self) -> Result<String> {
+        let patterns: Vec<String> = if !self.flag_regexp.is_empty() {
+            self.flag_regexp.iter().cloned().collect()
+        } else if let Some(ref file) = self.flag_file {
+            if file == "-" {
+                // We need two local variables here to get the lock
+                // lifetimes correct.
+                let stdin = io::stdin();
+                let result = stdin.lock().lines().collect();
+                try!(result)
             } else {
-                self.flag_regexp.iter().cloned().map(|pat| {
-                    self.word_pattern(pat)
-                }).collect::<Vec<String>>().join("|")
+                let f = try!(fs::File::open(&Path::new(file)));
+                try!(io::BufReader::new(f).lines().collect())
             }
         } else {
-            if self.flag_fixed_strings {
-                self.word_pattern(regex::quote(&self.arg_pattern))
-            } else {
-                self.word_pattern(self.arg_pattern.clone())
-            }
+            vec![self.arg_pattern.clone()]
+        };
+
+        if self.flag_fixed_strings {
+            Ok(patterns.into_iter().map(|p| {
+                self.word_pattern(regex::quote(&p))
+            }).collect::<Vec<String>>().join("|"))
+        } else {
+            Ok(patterns.into_iter().map(|p| {
+                self.word_pattern(p)
+            }).collect::<Vec<String>>().join("|"))
         }
     }
 
@@ -520,7 +537,7 @@ impl RawArgs {
         let casei =
             self.flag_ignore_case
             && !self.flag_case_sensitive;
-        GrepBuilder::new(&self.pattern())
+        GrepBuilder::new(&try!(self.pattern()))
             .case_smart(smart)
             .case_insensitive(casei)
             .line_terminator(self.eol())
diff --git a/tests/tests.rs b/tests/tests.rs
index 2a53c479..3481121b 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -902,6 +902,29 @@ clean!(regression_228, "test", ".", |wd: WorkDir, mut cmd: Command| {
     wd.assert_err(&mut cmd);
 });
 
+// See: https://github.com/BurntSushi/ripgrep/issues/7
+sherlock!(feature_7, "-fpat", "sherlock", |wd: WorkDir, mut cmd: Command| {
+    wd.create("pat", "Sherlock\nHolmes");
+    let lines: String = wd.stdout(&mut cmd);
+    let expected = "\
+For the Doctor Watsons of this world, as opposed to the Sherlock
+Holmeses, success in the province of detective work must always
+be, to a very large extent, the result of luck. Sherlock Holmes
+";
+    assert_eq!(lines, expected);
+});
+
+// See: https://github.com/BurntSushi/ripgrep/issues/7
+sherlock!(feature_7_dash, "-f-", ".", |wd: WorkDir, mut cmd: Command| {
+    let output = wd.pipe(&mut cmd, "Sherlock");
+    let lines = String::from_utf8_lossy(&output.stdout);
+    let expected = "\
+sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
+sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
+";
+    assert_eq!(lines, expected);
+});
+
 // See: https://github.com/BurntSushi/ripgrep/issues/20
 sherlock!(feature_20_no_filename, "Sherlock", ".",
 |wd: WorkDir, mut cmd: Command| {
diff --git a/tests/workdir.rs b/tests/workdir.rs
index 5ef3b72e..d9de0f8a 100644
--- a/tests/workdir.rs
+++ b/tests/workdir.rs
@@ -153,7 +153,41 @@ impl WorkDir {
 
     /// Gets the output of a command. If the command failed, then this panics.
     pub fn output(&self, cmd: &mut process::Command) -> process::Output {
-        let o = cmd.output().unwrap();
+        let output = cmd.output().unwrap();
+        self.expect_success(cmd, output)
+    }
+
+    /// Pipe `input` to a command, and collect the output.
+    pub fn pipe(
+        &self,
+        cmd: &mut process::Command,
+        input: &str
+    ) -> process::Output {
+        cmd.stdin(process::Stdio::piped());
+        cmd.stdout(process::Stdio::piped());
+        cmd.stderr(process::Stdio::piped());
+
+        let mut child = cmd.spawn().unwrap();
+
+        // Pipe input to child process using a separate thread to avoid
+        // risk of deadlock between parent and child process.
+        let mut stdin = child.stdin.take().expect("expected standard input");
+        let input = input.to_owned();
+        let worker = thread::spawn(move || {
+            write!(stdin, "{}", input)
+        });
+
+        let output = self.expect_success(cmd, child.wait_with_output().unwrap());
+        worker.join().unwrap().unwrap();
+        output
+    }
+
+    /// If `o` is not the output of a successful process run
+    fn expect_success(
+        &self,
+        cmd: &process::Command,
+        o: process::Output
+    ) -> process::Output {
         if !o.status.success() {
             let suggest =
                 if o.stderr.is_empty() {