1
0
mirror of https://github.com/BurntSushi/ripgrep.git synced 2025-04-14 00:58:43 +02:00

Merge pull request #227 from emk/issue-7

Allow specifying patterns with `-f FILE` and `-f-`
This commit is contained in:
Andrew Gallant 2016-11-15 18:15:05 -05:00 committed by GitHub
commit 39e1a0d694
3 changed files with 91 additions and 17 deletions

View File

@ -1,6 +1,7 @@
use std::cmp; use std::cmp;
use std::env; use std::env;
use std::io; use std::fs;
use std::io::{self, BufRead};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process; use std::process;
@ -34,6 +35,7 @@ use Result;
/// (TL;DR: The CLI parser is generated from the usage string below.) /// (TL;DR: The CLI parser is generated from the usage string below.)
const USAGE: &'static str = " const USAGE: &'static str = "
Usage: rg [options] -e PATTERN ... [<path> ...] Usage: rg [options] -e PATTERN ... [<path> ...]
rg [options] -f FILE [<path> ...]
rg [options] <pattern> [<path> ...] rg [options] <pattern> [<path> ...]
rg [options] --files [<path> ...] rg [options] --files [<path> ...]
rg [options] --type-list rg [options] --type-list
@ -107,6 +109,11 @@ Less common options:
--debug --debug
Show debug messages. 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 --files
Print each file that would be searched (but don't search). Print each file that would be searched (but don't search).
@ -242,6 +249,7 @@ pub struct RawArgs {
flag_count: bool, flag_count: bool,
flag_files_with_matches: bool, flag_files_with_matches: bool,
flag_debug: bool, flag_debug: bool,
flag_file: Option<String>,
flag_files: bool, flag_files: bool,
flag_follow: bool, flag_follow: bool,
flag_glob: Vec<String>, flag_glob: Vec<String>,
@ -479,23 +487,32 @@ impl RawArgs {
btypes.build().map_err(From::from) btypes.build().map_err(From::from)
} }
fn pattern(&self) -> String { fn pattern(&self) -> Result<String> {
if !self.flag_regexp.is_empty() { let patterns: Vec<String> = if !self.flag_regexp.is_empty() {
if self.flag_fixed_strings { self.flag_regexp.iter().cloned().collect()
self.flag_regexp.iter().cloned().map(|lit| { } else if let Some(ref file) = self.flag_file {
self.word_pattern(regex::quote(&lit)) if file == "-" {
}).collect::<Vec<String>>().join("|") // 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 { } else {
self.flag_regexp.iter().cloned().map(|pat| { let f = try!(fs::File::open(&Path::new(file)));
self.word_pattern(pat) try!(io::BufReader::new(f).lines().collect())
}).collect::<Vec<String>>().join("|")
} }
} else { } else {
if self.flag_fixed_strings { vec![self.arg_pattern.clone()]
self.word_pattern(regex::quote(&self.arg_pattern)) };
} else {
self.word_pattern(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 = let casei =
self.flag_ignore_case self.flag_ignore_case
&& !self.flag_case_sensitive; && !self.flag_case_sensitive;
GrepBuilder::new(&self.pattern()) GrepBuilder::new(&try!(self.pattern()))
.case_smart(smart) .case_smart(smart)
.case_insensitive(casei) .case_insensitive(casei)
.line_terminator(self.eol()) .line_terminator(self.eol())

View File

@ -902,6 +902,29 @@ clean!(regression_228, "test", ".", |wd: WorkDir, mut cmd: Command| {
wd.assert_err(&mut cmd); 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 // See: https://github.com/BurntSushi/ripgrep/issues/20
sherlock!(feature_20_no_filename, "Sherlock", ".", sherlock!(feature_20_no_filename, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| { |wd: WorkDir, mut cmd: Command| {

View File

@ -153,7 +153,41 @@ impl WorkDir {
/// Gets the output of a command. If the command failed, then this panics. /// Gets the output of a command. If the command failed, then this panics.
pub fn output(&self, cmd: &mut process::Command) -> process::Output { 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() { if !o.status.success() {
let suggest = let suggest =
if o.stderr.is_empty() { if o.stderr.is_empty() {