mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-06-05 16:21:15 -03:00
Add --all option to path
- Add --all option to path - Add tests - Add doc
This commit is contained in:
committed by
Johannes Altmanninger
parent
8c5de9acfb
commit
7fe92be405
@@ -13,7 +13,7 @@ Synopsis
|
|||||||
path extension GENERAL_OPTIONS [PATH ...]
|
path extension GENERAL_OPTIONS [PATH ...]
|
||||||
path filter GENERAL_OPTIONS [-v | --invert]
|
path filter GENERAL_OPTIONS [-v | --invert]
|
||||||
[-d] [-f] [-l] [-r] [-w] [-x]
|
[-d] [-f] [-l] [-r] [-w] [-x]
|
||||||
[(-t | --type) TYPE] [(-p | --perm) PERMISSION] [PATH ...]
|
[(-t | --type) TYPE] [(-p | --perm) PERMISSION] [--all] [PATH ...]
|
||||||
path is GENERAL_OPTIONS [(-v | --invert)] [(-t | --type) TYPE]
|
path is GENERAL_OPTIONS [(-v | --invert)] [(-t | --type) TYPE]
|
||||||
[-d] [-f] [-l] [-r] [-w] [-x]
|
[-d] [-f] [-l] [-r] [-w] [-x]
|
||||||
[(-p | --perm) PERMISSION] [PATH ...]
|
[(-p | --perm) PERMISSION] [PATH ...]
|
||||||
@@ -22,7 +22,7 @@ Synopsis
|
|||||||
path resolve GENERAL_OPTIONS [PATH ...]
|
path resolve GENERAL_OPTIONS [PATH ...]
|
||||||
path change-extension GENERAL_OPTIONS EXTENSION [PATH ...]
|
path change-extension GENERAL_OPTIONS EXTENSION [PATH ...]
|
||||||
path sort GENERAL_OPTIONS [-r | --reverse]
|
path sort GENERAL_OPTIONS [-r | --reverse]
|
||||||
[-u | --unique] [--key=basename|dirname|path] [PATH ...]
|
[-u | --unique] [--key=(basename | dirname | path)] [PATH ...]
|
||||||
|
|
||||||
GENERAL_OPTIONS
|
GENERAL_OPTIONS
|
||||||
[-z | --null-in] [-Z | --null-out] [-q | --quiet]
|
[-z | --null-in] [-Z | --null-out] [-q | --quiet]
|
||||||
@@ -148,7 +148,7 @@ Examples
|
|||||||
> echo $path$extension
|
> echo $path$extension
|
||||||
# reconstructs the original path again.
|
# reconstructs the original path again.
|
||||||
./foo.mp4
|
./foo.mp4
|
||||||
|
|
||||||
.. _cmd-path-filter:
|
.. _cmd-path-filter:
|
||||||
|
|
||||||
"filter" subcommand
|
"filter" subcommand
|
||||||
@@ -158,7 +158,7 @@ Examples
|
|||||||
|
|
||||||
path filter [-z | --null-in] [-Z | --null-out] [-q | --quiet] \
|
path filter [-z | --null-in] [-Z | --null-out] [-q | --quiet] \
|
||||||
[-d] [-f] [-l] [-r] [-w] [-x] \
|
[-d] [-f] [-l] [-r] [-w] [-x] \
|
||||||
[-v | --invert] [(-t | --type) TYPE] [(-p | --perm) PERMISSION] [PATH ...]
|
[-v | --invert] [(-t | --type) TYPE] [(-p | --perm) PERMISSION] [--all] [PATH ...]
|
||||||
|
|
||||||
``path filter`` returns all of the given paths that match the given checks. In all cases, the paths need to exist, nonexistent paths are always filtered.
|
``path filter`` returns all of the given paths that match the given checks. In all cases, the paths need to exist, nonexistent paths are always filtered.
|
||||||
|
|
||||||
@@ -180,6 +180,10 @@ When a path starts with ``-``, ``path filter`` will prepend ``./`` to avoid it b
|
|||||||
|
|
||||||
It returns 0 if at least one path passed the filter.
|
It returns 0 if at least one path passed the filter.
|
||||||
|
|
||||||
|
With ``--all``, return status 0 (true) if all paths pass the filter, and status 1 (false) if any path fails. This is equivalent to ``not path filter -v``. It produces no output, only a status.
|
||||||
|
|
||||||
|
When ``--all`` combined with ``--invert``, it returns status 0 (true) if all paths fail the filter and status 1 (false) if any path passes.
|
||||||
|
|
||||||
``path is`` is shorthand for ``path filter -q``, i.e. just checking without producing output, see :ref:`The is subcommand <cmd-path-is>`.
|
``path is`` is shorthand for ``path filter -q``, i.e. just checking without producing output, see :ref:`The is subcommand <cmd-path-is>`.
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
@@ -211,6 +215,9 @@ Examples
|
|||||||
>_ path filter -fx $PATH/*
|
>_ path filter -fx $PATH/*
|
||||||
# Prints all possible commands - the first entry of each name is what fish would execute!
|
# Prints all possible commands - the first entry of each name is what fish would execute!
|
||||||
|
|
||||||
|
>_ path filter --all /usr/bin /usr/argagagji
|
||||||
|
# This returns 1 (false) because not all paths pass the filter.
|
||||||
|
|
||||||
.. _cmd-path-is:
|
.. _cmd-path-is:
|
||||||
|
|
||||||
"is" subcommand
|
"is" subcommand
|
||||||
|
|||||||
@@ -154,6 +154,9 @@ struct Options<'args> {
|
|||||||
|
|
||||||
no_ext_valid: bool,
|
no_ext_valid: bool,
|
||||||
no_ext: bool,
|
no_ext: bool,
|
||||||
|
|
||||||
|
all_valid: bool,
|
||||||
|
all: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -201,14 +204,13 @@ fn construct_short_opts(opts: &Options) -> WString {
|
|||||||
if opts.no_ext_valid {
|
if opts.no_ext_valid {
|
||||||
short_opts.push('E');
|
short_opts.push('E');
|
||||||
}
|
}
|
||||||
|
|
||||||
short_opts
|
short_opts
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Note that several long flags share the same short flag. That is okay. The caller is expected
|
/// Note that several long flags share the same short flag. That is okay. The caller is expected
|
||||||
/// to indicate that a max of one of the long flags sharing a short flag is valid.
|
/// to indicate that a max of one of the long flags sharing a short flag is valid.
|
||||||
/// Remember: adjust the completions in share/completions/ when options change
|
/// Remember: adjust the completions in share/completions/ when options change
|
||||||
const LONG_OPTIONS: [WOption<'static>; 11] = [
|
const LONG_OPTIONS: [WOption<'static>; 12] = [
|
||||||
wopt(L!("quiet"), NoArgument, 'q'),
|
wopt(L!("quiet"), NoArgument, 'q'),
|
||||||
wopt(L!("null-in"), NoArgument, 'z'),
|
wopt(L!("null-in"), NoArgument, 'z'),
|
||||||
wopt(L!("null-out"), NoArgument, 'Z'),
|
wopt(L!("null-out"), NoArgument, 'Z'),
|
||||||
@@ -220,6 +222,7 @@ fn construct_short_opts(opts: &Options) -> WString {
|
|||||||
wopt(L!("unique"), NoArgument, 'u'),
|
wopt(L!("unique"), NoArgument, 'u'),
|
||||||
wopt(L!("key"), RequiredArgument, NON_OPTION_CHAR),
|
wopt(L!("key"), RequiredArgument, NON_OPTION_CHAR),
|
||||||
wopt(L!("no-extension"), NoArgument, 'E'),
|
wopt(L!("no-extension"), NoArgument, 'E'),
|
||||||
|
wopt(L!("all"), NoArgument, '\x02'),
|
||||||
];
|
];
|
||||||
|
|
||||||
fn parse_opts<'args>(
|
fn parse_opts<'args>(
|
||||||
@@ -339,6 +342,11 @@ fn parse_opts<'args>(
|
|||||||
opts.key = w.woptarg;
|
opts.key = w.woptarg;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
'\x02' if opts.all_valid => {
|
||||||
|
opts.all = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
path_unknown_option(parser, streams, cmd, args_read[w.wopt_index - 1]);
|
path_unknown_option(parser, streams, cmd, args_read[w.wopt_index - 1]);
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
@@ -846,6 +854,7 @@ fn path_filter_maybe_is(
|
|||||||
opts.types_valid = true;
|
opts.types_valid = true;
|
||||||
opts.perms_valid = true;
|
opts.perms_valid = true;
|
||||||
opts.invert_valid = true;
|
opts.invert_valid = true;
|
||||||
|
opts.all_valid = true;
|
||||||
let mut optind = 0;
|
let mut optind = 0;
|
||||||
|
|
||||||
parse_opts(&mut opts, &mut optind, 0, args, parser, streams)?;
|
parse_opts(&mut opts, &mut optind, 0, args, parser, streams)?;
|
||||||
@@ -873,7 +882,10 @@ fn path_filter_maybe_is(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
for (arg, _) in arguments.filter(|(f, _)| {
|
// Collect arguments into a Vec so we can use .len()
|
||||||
|
let arguments_vec: Vec<_> = arguments.collect();
|
||||||
|
|
||||||
|
for (arg, _) in arguments_vec.iter().cloned().filter(|(f, _)| {
|
||||||
(opts.perms.is_none() && opts.types.is_none())
|
(opts.perms.is_none() && opts.types.is_none())
|
||||||
|| (filter_path(&opts, f, uid, gid) != opts.invert)
|
|| (filter_path(&opts, f, uid, gid) != opts.invert)
|
||||||
}) {
|
}) {
|
||||||
@@ -881,10 +893,20 @@ fn path_filter_maybe_is(
|
|||||||
if opts.perms.is_none() && opts.types.is_none() {
|
if opts.perms.is_none() && opts.types.is_none() {
|
||||||
let ok = waccess(&arg, F_OK) == 0;
|
let ok = waccess(&arg, F_OK) == 0;
|
||||||
if ok == opts.invert {
|
if ok == opts.invert {
|
||||||
|
// For --all, fail early if any path does not match the filter.
|
||||||
|
if opts.all {
|
||||||
|
return Err(STATUS_CMD_ERROR);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n_transformed += 1;
|
||||||
|
|
||||||
|
if opts.all {
|
||||||
|
// For --all, do not output paths, just check all must match.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// We *know* this is a filename,
|
// We *know* this is a filename,
|
||||||
// and so if it starts with a `-` we *know* it is relative
|
// and so if it starts with a `-` we *know* it is relative
|
||||||
// to $PWD. So we can add `./`.
|
// to $PWD. So we can add `./`.
|
||||||
@@ -895,12 +917,16 @@ fn path_filter_maybe_is(
|
|||||||
} else {
|
} else {
|
||||||
path_out(streams, &opts, arg);
|
path_out(streams, &opts, arg);
|
||||||
}
|
}
|
||||||
n_transformed += 1;
|
|
||||||
if opts.quiet {
|
if opts.quiet {
|
||||||
return Ok(SUCCESS);
|
return Ok(SUCCESS);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.all && n_transformed != arguments_vec.len() {
|
||||||
|
// We have a filter and some paths didn't match.
|
||||||
|
// Return Err if we have a filter and some paths didn't match.
|
||||||
|
return Err(STATUS_CMD_ERROR);
|
||||||
|
}
|
||||||
if n_transformed > 0 {
|
if n_transformed > 0 {
|
||||||
Ok(SUCCESS)
|
Ok(SUCCESS)
|
||||||
} else {
|
} else {
|
||||||
@@ -922,7 +948,6 @@ pub fn path(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> Bui
|
|||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
};
|
};
|
||||||
let argc = args.len();
|
let argc = args.len();
|
||||||
|
|
||||||
if argc <= 1 {
|
if argc <= 1 {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
|
|||||||
@@ -105,6 +105,21 @@ path filter -vf bin argagagji
|
|||||||
# CHECK: bin
|
# CHECK: bin
|
||||||
# CHECK: argagagji
|
# CHECK: argagagji
|
||||||
|
|
||||||
|
# With --all, return true if all paths are passed.
|
||||||
|
path filter --all bin bin/bash
|
||||||
|
echo $status
|
||||||
|
# CHECK: 0
|
||||||
|
path filter --all bin argagagji
|
||||||
|
echo $status
|
||||||
|
# CHECK: 1
|
||||||
|
# With --all and --invert, return true if none of paths is passed.
|
||||||
|
path filter --all --invert bin bin/bash
|
||||||
|
echo $status
|
||||||
|
# CHECK: 1
|
||||||
|
path filter --all --invert argagagji argagagji2
|
||||||
|
echo $status
|
||||||
|
# CHECK: 0
|
||||||
|
|
||||||
path filter --type file bin bin/fish
|
path filter --type file bin bin/fish
|
||||||
# Only fish is a file
|
# Only fish is a file
|
||||||
# CHECK: bin/fish
|
# CHECK: bin/fish
|
||||||
@@ -273,8 +288,6 @@ path sort --unique --key=basename {def,abc}/{456,123,789} def/{abc,def,0} abc/{f
|
|||||||
# CHECK: def/def
|
# CHECK: def/def
|
||||||
# CHECK: abc/foo
|
# CHECK: abc/foo
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Symlink loop.
|
# Symlink loop.
|
||||||
# It goes brrr.
|
# It goes brrr.
|
||||||
ln -s target link
|
ln -s target link
|
||||||
@@ -288,13 +301,12 @@ test (path resolve link) = (pwd -P)/link
|
|||||||
and echo link resolves to link
|
and echo link resolves to link
|
||||||
# CHECK: link resolves to link
|
# CHECK: link resolves to link
|
||||||
|
|
||||||
|
|
||||||
# path mtime
|
# path mtime
|
||||||
# These tests deal with *time*, so we have to account
|
# These tests deal with *time*, so we have to account
|
||||||
# for slow systems (like CI).
|
# for slow systems (like CI).
|
||||||
# So we should only test with a lot of slack.
|
# So we should only test with a lot of slack.
|
||||||
|
|
||||||
echo bananana >> foo
|
echo bananana >>foo
|
||||||
test (math abs (date +%s) - (path mtime foo)) -lt 20
|
test (math abs (date +%s) - (path mtime foo)) -lt 20
|
||||||
or echo MTIME IS BOGUS
|
or echo MTIME IS BOGUS
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user