string: Allow collect --allow-empty to avoid empty ellision (#8054)

* string: Allow `collect --no-empty` to avoid empty ellision

Currently we still have that issue where

    test -n (thing | string collect)

can return true if `thing` doesn't print anything, because the
collected argument will still be removed.

So, what we do is allow `--no-empty` to be used, in which case we
print one empty argument.

This means

    test -n (thing | string collect -n)

can now be safely used.

"no-empty" isn't the best name for this flag, but string's design
really incentivizes reusing names, and it's not *terrible*.

* Switch to `--allow-empty`

`--no-empty` does the exact opposite for `string split` and split0.

Since `-a`/`--allow-empty` already exists, use it.
This commit is contained in:
Fabian Homborg 2021-07-09 21:20:58 +02:00 committed by GitHub
parent 32826d3596
commit 0e1f5108ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 27 additions and 1 deletions

View File

@ -8,7 +8,7 @@ Synopsis
::
string collect [(-N | --no-trim-newlines)] [STRING...]
string collect [(-a | --allow-empty)] [(-N | --no-trim-newlines)] [STRING...]
.. END SYNOPSIS
@ -23,6 +23,8 @@ If invoked with multiple arguments instead of input, ``string collect`` preserve
Any trailing newlines on the input are trimmed, just as with ``"$(cmd)"`` substitution in sh. ``--no-trim-newlines`` can be used to disable this behavior, which may be useful when running a command such as ``set contents (cat filename | string collect -N)``.
With ``--allow-empty``, ``string collect`` always prints one (empty) argument. This can be used to prevent an argument from disappearing.
.. END DESCRIPTION
Examples
@ -43,4 +45,7 @@ Examples
three
"
>_ echo foo(true | string collect --allow-empty)bar
foobar
.. END EXAMPLES

View File

@ -1527,6 +1527,7 @@ static int string_split0(parser_t &parser, io_streams_t &streams, int argc, cons
static int string_collect(parser_t &parser, io_streams_t &streams, int argc, const wchar_t **argv) {
options_t opts;
opts.allow_empty_valid = true;
opts.no_trim_newlines_valid = true;
int optind;
int retval = parse_opts(&opts, &optind, 0, argc, argv, parser, streams);
@ -1546,6 +1547,14 @@ static int string_collect(parser_t &parser, io_streams_t &streams, int argc, con
appended += len;
}
// If we haven't printed anything and "no_empty" is set,
// print something empty. Helps with empty ellision:
// echo (true | string collect --allow-empty)"bar"
// prints "bar".
if (opts.allow_empty && appended == 0) {
streams.out.append_with_separation(L"", 0, separation_type_t::explicitly);
}
return appended > 0 ? STATUS_CMD_OK : STATUS_CMD_ERROR;
}

View File

@ -667,6 +667,18 @@ string collect -N '' >/dev/null; and echo unexpected success; or echo expected f
string collect \n\n >/dev/null; and echo unexpected success; or echo expected failure
# CHECK: expected failure
echo "foo"(true | string collect --allow-empty)"bar"
# CHECK: foobar
test -z (string collect)
and echo Nothing
# CHECK: Nothing
test -n (string collect)
and echo Something
# CHECK: Something
test -n (string collect -a)
or echo No, actually nothing
# CHECK: No, actually nothing
# string collect in functions
# This function outputs some newline-separated content, and some
# explicitly un-separated content.