mirror of https://github.com/sharkdp/fd.git
Add docs and tests for --format
Also fix bug where we didn't strip leading cwd.
This commit is contained in:
parent
985f2b1374
commit
1d8ad3d13d
|
@ -363,6 +363,30 @@ Set the path separator to use when printing file paths. The default is the OS-sp
|
||||||
Provide paths to search as an alternative to the positional \fIpath\fR argument. Changes the usage to
|
Provide paths to search as an alternative to the positional \fIpath\fR argument. Changes the usage to
|
||||||
\'fd [FLAGS/OPTIONS] \-\-search\-path PATH \-\-search\-path PATH2 [PATTERN]\'
|
\'fd [FLAGS/OPTIONS] \-\-search\-path PATH \-\-search\-path PATH2 [PATTERN]\'
|
||||||
.TP
|
.TP
|
||||||
|
.BI "\-\-format " fmt
|
||||||
|
Specify a template string that is used for printing a line for each file found.
|
||||||
|
|
||||||
|
The following placeholders are substituted into the string for each file before printing:
|
||||||
|
.RS
|
||||||
|
.IP {}
|
||||||
|
path (of the current search result)
|
||||||
|
.IP {/}
|
||||||
|
basename
|
||||||
|
.IP {//}
|
||||||
|
parent directory
|
||||||
|
.IP {.}
|
||||||
|
path without file extension
|
||||||
|
.IP {/.}
|
||||||
|
basename without file extension
|
||||||
|
.IP {{
|
||||||
|
literal '{' (an escape sequence)
|
||||||
|
.IP }}
|
||||||
|
literal '}' (an escape sequence)
|
||||||
|
.P
|
||||||
|
Notice that you can use "{{" and "}}" to escape "{" and "}" respectively, which is especially
|
||||||
|
useful if you need to include the literal text of one of the above placeholders.
|
||||||
|
.RE
|
||||||
|
.TP
|
||||||
.BI "\-x, \-\-exec " command
|
.BI "\-x, \-\-exec " command
|
||||||
.RS
|
.RS
|
||||||
Execute
|
Execute
|
||||||
|
@ -383,29 +407,12 @@ If parallelism is enabled, the order commands will be executed in is non-determi
|
||||||
--threads=1, the order is determined by the operating system and may not be what you expect. Thus, it is
|
--threads=1, the order is determined by the operating system and may not be what you expect. Thus, it is
|
||||||
recommended that you don't rely on any ordering of the results.
|
recommended that you don't rely on any ordering of the results.
|
||||||
|
|
||||||
The following placeholders are substituted before the command is executed:
|
Before executing the command, any placeholder patterns in the command are replaced with the
|
||||||
.RS
|
corresponding values for the current file. The same placeholders are used as in the "\-\-format"
|
||||||
.IP {}
|
option.
|
||||||
path (of the current search result)
|
|
||||||
.IP {/}
|
|
||||||
basename
|
|
||||||
.IP {//}
|
|
||||||
parent directory
|
|
||||||
.IP {.}
|
|
||||||
path without file extension
|
|
||||||
.IP {/.}
|
|
||||||
basename without file extension
|
|
||||||
.IP {{
|
|
||||||
literal '{' (an escape sequence)
|
|
||||||
.IP }}
|
|
||||||
literal '}' (an escape sequence)
|
|
||||||
.RE
|
|
||||||
|
|
||||||
If no placeholder is present, an implicit "{}" at the end is assumed.
|
If no placeholder is present, an implicit "{}" at the end is assumed.
|
||||||
|
|
||||||
Notice that you can use "{{" and "}}" to escape "{" and "}" respectively, which is especially
|
|
||||||
useful if you need to include the literal text of one of the above placeholders.
|
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
- find all *.zip files and unzip them:
|
- find all *.zip files and unzip them:
|
||||||
|
@ -429,19 +436,9 @@ once, with all search results as arguments.
|
||||||
|
|
||||||
The order of the arguments is non-deterministic and should not be relied upon.
|
The order of the arguments is non-deterministic and should not be relied upon.
|
||||||
|
|
||||||
One of the following placeholders is substituted before the command is executed:
|
This uses the same placeholders as "\-\-format" and "\-\-exec", but instead of expanding
|
||||||
.RS
|
once per command invocation each argument containing a placeholder is expanding for every
|
||||||
.IP {}
|
file in a batch and passed as separate arguments.
|
||||||
path (of all search results)
|
|
||||||
.IP {/}
|
|
||||||
basename
|
|
||||||
.IP {//}
|
|
||||||
parent directory
|
|
||||||
.IP {.}
|
|
||||||
path without file extension
|
|
||||||
.IP {/.}
|
|
||||||
basename without file extension
|
|
||||||
.RE
|
|
||||||
|
|
||||||
If no placeholder is present, an implicit "{}" at the end is assumed.
|
If no placeholder is present, an implicit "{}" at the end is assumed.
|
||||||
|
|
||||||
|
|
|
@ -209,3 +209,73 @@ fn token_from_pattern_id(id: u32) -> Token {
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod fmt_tests {
|
||||||
|
use super::*;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_no_placeholders() {
|
||||||
|
let templ = FormatTemplate::parse("This string has no placeholders");
|
||||||
|
assert_eq!(
|
||||||
|
templ,
|
||||||
|
FormatTemplate::Text("This string has no placeholders".into())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_only_brace_escapes() {
|
||||||
|
let templ = FormatTemplate::parse("This string only has escapes like {{ and }}");
|
||||||
|
assert_eq!(
|
||||||
|
templ,
|
||||||
|
FormatTemplate::Text("This string only has escapes like { and }".into())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn all_placeholders() {
|
||||||
|
use Token::*;
|
||||||
|
|
||||||
|
let templ = FormatTemplate::parse(
|
||||||
|
"{{path={} \
|
||||||
|
basename={/} \
|
||||||
|
parent={//} \
|
||||||
|
noExt={.} \
|
||||||
|
basenameNoExt={/.} \
|
||||||
|
}}",
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
templ,
|
||||||
|
FormatTemplate::Tokens(vec![
|
||||||
|
Text("{path=".into()),
|
||||||
|
Placeholder,
|
||||||
|
Text(" basename=".into()),
|
||||||
|
Basename,
|
||||||
|
Text(" parent=".into()),
|
||||||
|
Parent,
|
||||||
|
Text(" noExt=".into()),
|
||||||
|
NoExt,
|
||||||
|
Text(" basenameNoExt=".into()),
|
||||||
|
BasenameNoExt,
|
||||||
|
Text(" }".into()),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut path = PathBuf::new();
|
||||||
|
path.push("a");
|
||||||
|
path.push("folder");
|
||||||
|
path.push("file.txt");
|
||||||
|
|
||||||
|
let expanded = templ.generate(&path, Some("/")).into_string().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
expanded,
|
||||||
|
"{path=a/folder/file.txt \
|
||||||
|
basename=file.txt \
|
||||||
|
parent=a/folder \
|
||||||
|
noExt=a/folder/file \
|
||||||
|
basenameNoExt=file }"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -66,7 +66,10 @@ fn print_entry_format<W: Write>(
|
||||||
format: &FormatTemplate,
|
format: &FormatTemplate,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
let separator = if config.null_separator { "\0" } else { "\n" };
|
let separator = if config.null_separator { "\0" } else { "\n" };
|
||||||
let output = format.generate(entry.path(), config.path_separator.as_deref());
|
let output = format.generate(
|
||||||
|
entry.stripped_path(&config),
|
||||||
|
config.path_separator.as_deref(),
|
||||||
|
);
|
||||||
// TODO: support writing raw bytes on unix?
|
// TODO: support writing raw bytes on unix?
|
||||||
write!(stdout, "{}{}", output.to_string_lossy(), separator)
|
write!(stdout, "{}{}", output.to_string_lossy(), separator)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1622,6 +1622,66 @@ fn test_excludes() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn format() {
|
||||||
|
let te = TestEnv::new(DEFAULT_DIRS, DEFAULT_FILES);
|
||||||
|
|
||||||
|
te.assert_output(
|
||||||
|
&["--format", "path={}", "--path-separator=/"],
|
||||||
|
"path=a.foo
|
||||||
|
path=e1 e2
|
||||||
|
path=one
|
||||||
|
path=one/b.foo
|
||||||
|
path=one/two
|
||||||
|
path=one/two/C.Foo2
|
||||||
|
path=one/two/c.foo
|
||||||
|
path=one/two/three
|
||||||
|
path=one/two/three/d.foo
|
||||||
|
path=one/two/three/directory_foo
|
||||||
|
path=symlink",
|
||||||
|
);
|
||||||
|
|
||||||
|
te.assert_output(
|
||||||
|
&["foo", "--format", "noExt={.}", "--path-separator=/"],
|
||||||
|
"noExt=a
|
||||||
|
noExt=one/b
|
||||||
|
noExt=one/two/C
|
||||||
|
noExt=one/two/c
|
||||||
|
noExt=one/two/three/d
|
||||||
|
noExt=one/two/three/directory_foo",
|
||||||
|
);
|
||||||
|
|
||||||
|
te.assert_output(
|
||||||
|
&["foo", "--format", "basename={/}", "--path-separator=/"],
|
||||||
|
"basename=a.foo
|
||||||
|
basename=b.foo
|
||||||
|
basename=C.Foo2
|
||||||
|
basename=c.foo
|
||||||
|
basename=d.foo
|
||||||
|
basename=directory_foo",
|
||||||
|
);
|
||||||
|
|
||||||
|
te.assert_output(
|
||||||
|
&["foo", "--format", "name={/.}", "--path-separator=/"],
|
||||||
|
"name=a
|
||||||
|
name=b
|
||||||
|
name=C
|
||||||
|
name=c
|
||||||
|
name=d
|
||||||
|
name=directory_foo",
|
||||||
|
);
|
||||||
|
|
||||||
|
te.assert_output(
|
||||||
|
&["foo", "--format", "parent={//}", "--path-separator=/"],
|
||||||
|
"parent=.
|
||||||
|
parent=one
|
||||||
|
parent=one/two
|
||||||
|
parent=one/two
|
||||||
|
parent=one/two/three
|
||||||
|
parent=one/two/three",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Shell script execution (--exec)
|
/// Shell script execution (--exec)
|
||||||
#[test]
|
#[test]
|
||||||
fn test_exec() {
|
fn test_exec() {
|
||||||
|
|
Loading…
Reference in New Issue