mirror of
https://github.com/sharkdp/fd
synced 2024-10-06 07:49:18 +00:00
Add support for exclude-patterns
* Add `--exclude`/`-E` option. * Support for multiple exclude patterns Example: ``` bash > fd --exclude 'tests/**/*.rs' mod src/exec/mod.rs src/fshelper/mod.rs src/lscolors/mod.rs ``` Closes #89
This commit is contained in:
parent
19afb15a98
commit
5ad69fb2fb
35
src/app.rs
35
src/app.rs
|
@ -81,6 +81,22 @@ pub fn build_app() -> App<'static, 'static> {
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.value_name("ext"),
|
.value_name("ext"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
arg("exec")
|
||||||
|
.long("exec")
|
||||||
|
.short("x")
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("cmd"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
arg("exclude")
|
||||||
|
.long("exclude")
|
||||||
|
.short("E")
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("pattern")
|
||||||
|
.number_of_values(1)
|
||||||
|
.multiple(true),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
arg("color")
|
arg("color")
|
||||||
.long("color")
|
.long("color")
|
||||||
|
@ -103,13 +119,6 @@ pub fn build_app() -> App<'static, 'static> {
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.hidden(true),
|
.hidden(true),
|
||||||
)
|
)
|
||||||
.arg(
|
|
||||||
arg("exec")
|
|
||||||
.long("exec")
|
|
||||||
.short("x")
|
|
||||||
.takes_value(true)
|
|
||||||
.value_name("cmd"),
|
|
||||||
)
|
|
||||||
.arg(arg("pattern"))
|
.arg(arg("pattern"))
|
||||||
.arg(arg("path"))
|
.arg(arg("path"))
|
||||||
}
|
}
|
||||||
|
@ -158,6 +167,9 @@ fn usage() -> HashMap<&'static str, Help> {
|
||||||
'f' or 'file': regular files\n \
|
'f' or 'file': regular files\n \
|
||||||
'd' or 'directory': directories\n \
|
'd' or 'directory': directories\n \
|
||||||
'l' or 'symlink': symbolic links");
|
'l' or 'symlink': symbolic links");
|
||||||
|
doc!(h, "extension"
|
||||||
|
, "Filter by file extension"
|
||||||
|
, "(Additionally) filter search results by their file extension.");
|
||||||
doc!(h, "exec"
|
doc!(h, "exec"
|
||||||
, "Execute the given command for each search result"
|
, "Execute the given command for each search result"
|
||||||
, "Execute the given command for each search result.\n\
|
, "Execute the given command for each search result.\n\
|
||||||
|
@ -167,10 +179,11 @@ fn usage() -> HashMap<&'static str, Help> {
|
||||||
'{.}': removes the extension from the input\n \
|
'{.}': removes the extension from the input\n \
|
||||||
'{/}': places the basename of the input\n \
|
'{/}': places the basename of the input\n \
|
||||||
'{//}': places the parent of the input\n \
|
'{//}': places the parent of the input\n \
|
||||||
'{/.}': places the basename of the input, without the extension\n");
|
'{/.}': places the basename of the input, without the extension");
|
||||||
doc!(h, "extension"
|
doc!(h, "exclude"
|
||||||
, "Filter by file extension"
|
, "Exclude entries that match the given glob pattern."
|
||||||
, "(Additionally) filter search results by their file extension.");
|
, "Exclude files/directories that match the given glob pattern. This overrides any \
|
||||||
|
other ignore logic. Multiple exclude patterns can be specified.");
|
||||||
doc!(h, "color"
|
doc!(h, "color"
|
||||||
, "When to use colors: never, *auto*, always"
|
, "When to use colors: never, *auto*, always"
|
||||||
, "Declare when to use color for the pattern match output:\n \
|
, "Declare when to use color for the pattern match output:\n \
|
||||||
|
|
|
@ -77,6 +77,9 @@ pub struct FdOptions {
|
||||||
|
|
||||||
/// If a value is supplied, each item found will be used to generate and execute commands.
|
/// If a value is supplied, each item found will be used to generate and execute commands.
|
||||||
pub command: Option<TokenizedCommand>,
|
pub command: Option<TokenizedCommand>,
|
||||||
|
|
||||||
|
/// A list of glob patterns that should be excluded from the search.
|
||||||
|
pub exclude_patterns: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Print error message to stderr and exit with status `1`.
|
/// Print error message to stderr and exit with status `1`.
|
||||||
|
|
|
@ -141,6 +141,10 @@ fn main() {
|
||||||
e.trim_left_matches('.').to_lowercase()
|
e.trim_left_matches('.').to_lowercase()
|
||||||
}),
|
}),
|
||||||
command,
|
command,
|
||||||
|
exclude_patterns: matches
|
||||||
|
.values_of("exclude")
|
||||||
|
.map(|v| v.map(|p| String::from("!") + p).collect())
|
||||||
|
.unwrap_or(vec![]),
|
||||||
};
|
};
|
||||||
|
|
||||||
match RegexBuilder::new(pattern)
|
match RegexBuilder::new(pattern)
|
||||||
|
|
14
src/walk.rs
14
src/walk.rs
|
@ -18,6 +18,7 @@ use std::thread;
|
||||||
use std::time;
|
use std::time;
|
||||||
|
|
||||||
use ignore::{self, WalkBuilder};
|
use ignore::{self, WalkBuilder};
|
||||||
|
use ignore::overrides::OverrideBuilder;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
/// The receiver thread can either be buffering results or directly streaming to the console.
|
/// The receiver thread can either be buffering results or directly streaming to the console.
|
||||||
|
@ -48,6 +49,18 @@ pub fn scan(root: &Path, pattern: Arc<Regex>, config: Arc<FdOptions>) {
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
let threads = config.threads;
|
let threads = config.threads;
|
||||||
|
|
||||||
|
let mut override_builder = OverrideBuilder::new(root);
|
||||||
|
|
||||||
|
for pattern in config.exclude_patterns.iter() {
|
||||||
|
let res = override_builder.add(pattern);
|
||||||
|
if res.is_err() {
|
||||||
|
error(&format!("Error: malformed exclude pattern '{}'", pattern));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let overrides = override_builder.build().unwrap_or_else(|_| {
|
||||||
|
error("Mismatch in exclude patterns");
|
||||||
|
});
|
||||||
|
|
||||||
let walker = WalkBuilder::new(root)
|
let walker = WalkBuilder::new(root)
|
||||||
.hidden(config.ignore_hidden)
|
.hidden(config.ignore_hidden)
|
||||||
.ignore(config.read_ignore)
|
.ignore(config.read_ignore)
|
||||||
|
@ -55,6 +68,7 @@ pub fn scan(root: &Path, pattern: Arc<Regex>, config: Arc<FdOptions>) {
|
||||||
.parents(config.read_ignore)
|
.parents(config.read_ignore)
|
||||||
.git_global(config.read_ignore)
|
.git_global(config.read_ignore)
|
||||||
.git_exclude(config.read_ignore)
|
.git_exclude(config.read_ignore)
|
||||||
|
.overrides(overrides)
|
||||||
.follow_links(config.follow_links)
|
.follow_links(config.follow_links)
|
||||||
.max_depth(config.max_depth)
|
.max_depth(config.max_depth)
|
||||||
.threads(threads)
|
.threads(threads)
|
||||||
|
|
|
@ -538,3 +538,50 @@ fn test_symlink() {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Exclude patterns (--exclude)
|
||||||
|
#[test]
|
||||||
|
fn test_excludes() {
|
||||||
|
let te = TestEnv::new();
|
||||||
|
|
||||||
|
te.assert_output(
|
||||||
|
&["--exclude", "*.foo"],
|
||||||
|
"one
|
||||||
|
one/two
|
||||||
|
one/two/C.Foo2
|
||||||
|
one/two/three
|
||||||
|
one/two/three/directory_foo
|
||||||
|
symlink",
|
||||||
|
);
|
||||||
|
|
||||||
|
te.assert_output(
|
||||||
|
&["--exclude", "*.foo", "--exclude", "*.Foo2"],
|
||||||
|
"one
|
||||||
|
one/two
|
||||||
|
one/two/three
|
||||||
|
one/two/three/directory_foo
|
||||||
|
symlink",
|
||||||
|
);
|
||||||
|
|
||||||
|
te.assert_output(
|
||||||
|
&["--exclude", "*.foo", "--exclude", "*.Foo2", "foo"],
|
||||||
|
"one/two/three/directory_foo",
|
||||||
|
);
|
||||||
|
|
||||||
|
te.assert_output(
|
||||||
|
&["--exclude", "one/two", "foo"],
|
||||||
|
"a.foo
|
||||||
|
one/b.foo",
|
||||||
|
);
|
||||||
|
|
||||||
|
te.assert_output(
|
||||||
|
&["--exclude", "one/**/*.foo"],
|
||||||
|
"a.foo
|
||||||
|
one
|
||||||
|
one/two
|
||||||
|
one/two/C.Foo2
|
||||||
|
one/two/three
|
||||||
|
one/two/three/directory_foo
|
||||||
|
symlink",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue