mirror of
https://github.com/fish-shell/fish-shell.git
synced 2025-03-15 23:22:53 +08:00
Add string split --fields
This commit is contained in:
parent
a29bc127ce
commit
7cb1d3a646
@ -8,8 +8,8 @@ Synopsis
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
string split [(-m | --max) MAX] [(-n | --no-empty)] [(-q | --quiet)] [(-r | --right)] SEP [STRING...]
|
string split [(-f | --fields) FIELDS] [(-m | --max) MAX] [(-n | --no-empty)] [(-q | --quiet)] [(-r | --right)] SEP [STRING...]
|
||||||
string split0 [(-m | --max) MAX] [(-n | --no-empty)] [(-q | --quiet)] [(-r | --right)] [STRING...]
|
string split0 [(-f | --fields) FIELDS] [(-m | --max) MAX] [(-n | --no-empty)] [(-q | --quiet)] [(-r | --right)] [STRING...]
|
||||||
|
|
||||||
.. END SYNOPSIS
|
.. END SYNOPSIS
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ Description
|
|||||||
|
|
||||||
.. BEGIN DESCRIPTION
|
.. BEGIN DESCRIPTION
|
||||||
|
|
||||||
``string split`` splits each STRING on the separator SEP, which can be an empty string. If ``-m`` or ``--max`` is specified, at most MAX splits are done on each STRING. If ``-r`` or ``--right`` is given, splitting is performed right-to-left. This is useful in combination with ``-m`` or ``--max``. With ``-n`` or ``--no-empty``, empty results are excluded from consideration (e.g. ``hello\n\nworld`` would expand to two strings and not three). Exit status: 0 if at least one split was performed, or 1 otherwise.
|
``string split`` splits each STRING on the separator SEP, which can be an empty string. If ``-m`` or ``--max`` is specified, at most MAX splits are done on each STRING. If ``-r`` or ``--right`` is given, splitting is performed right-to-left. This is useful in combination with ``-m`` or ``--max``. With ``-n`` or ``--no-empty``, empty results are excluded from consideration (e.g. ``hello\n\nworld`` would expand to two strings and not three). Use ``-f`` or ``--fields`` to print out specific fields. Exit status: 0 if at least one split was performed, or 1 otherwise.
|
||||||
|
|
||||||
See also the ``--delimiter`` option of the :ref:`read <cmd-read>` command.
|
See also the ``--delimiter`` option of the :ref:`read <cmd-read>` command.
|
||||||
|
|
||||||
@ -49,6 +49,11 @@ Examples
|
|||||||
b
|
b
|
||||||
c
|
c
|
||||||
|
|
||||||
|
>_ string split -f1,3 '' abc
|
||||||
|
a
|
||||||
|
c
|
||||||
|
|
||||||
|
|
||||||
NUL Delimited Examples
|
NUL Delimited Examples
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains --
|
|||||||
complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a split
|
complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a split
|
||||||
complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a split0
|
complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a split0
|
||||||
complete -x -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s m -l max -a "(seq 1 10)" -d "Specify maximum number of splits"
|
complete -x -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s m -l max -a "(seq 1 10)" -d "Specify maximum number of splits"
|
||||||
|
complete -x -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s f -l fields -a "(seq 1 10)" -d "Specify fields"
|
||||||
complete -f -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s r -l right -d "Split right-to-left"
|
complete -f -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s r -l right -d "Split right-to-left"
|
||||||
complete -f -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s n -l no-empty -d "Empty results excluded"
|
complete -f -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s n -l no-empty -d "Empty results excluded"
|
||||||
complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a collect
|
complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a collect
|
||||||
|
@ -155,6 +155,7 @@ typedef struct { //!OCLINT(too many fields)
|
|||||||
bool style_valid = false;
|
bool style_valid = false;
|
||||||
bool no_empty_valid = false;
|
bool no_empty_valid = false;
|
||||||
bool no_trim_newlines_valid = false;
|
bool no_trim_newlines_valid = false;
|
||||||
|
bool fields_valid = false;
|
||||||
|
|
||||||
bool all = false;
|
bool all = false;
|
||||||
bool entire = false;
|
bool entire = false;
|
||||||
@ -177,6 +178,8 @@ typedef struct { //!OCLINT(too many fields)
|
|||||||
long start = 0;
|
long start = 0;
|
||||||
long end = 0;
|
long end = 0;
|
||||||
|
|
||||||
|
std::vector<int> fields;
|
||||||
|
|
||||||
const wchar_t *chars_to_trim = L" \f\n\r\t\v";
|
const wchar_t *chars_to_trim = L" \f\n\r\t\v";
|
||||||
const wchar_t *arg1 = nullptr;
|
const wchar_t *arg1 = nullptr;
|
||||||
const wchar_t *arg2 = nullptr;
|
const wchar_t *arg2 = nullptr;
|
||||||
@ -267,6 +270,19 @@ static int handle_flag_f(wchar_t **argv, parser_t &parser, io_streams_t &streams
|
|||||||
if (opts->filter_valid) {
|
if (opts->filter_valid) {
|
||||||
opts->filter = true;
|
opts->filter = true;
|
||||||
return STATUS_CMD_OK;
|
return STATUS_CMD_OK;
|
||||||
|
} else if (opts->fields_valid) {
|
||||||
|
for (const wcstring &s : split_string(w.woptarg, L',')) {
|
||||||
|
int field = fish_wcstoi(wcsdup(s.c_str()));
|
||||||
|
if (field <= 0 || field == INT_MIN || errno == ERANGE) {
|
||||||
|
string_error(streams, _(L"%ls: Invalid fields value '%ls'\n"), argv[0], w.woptarg);
|
||||||
|
return STATUS_INVALID_ARGS;
|
||||||
|
} else if (errno) {
|
||||||
|
string_error(streams, BUILTIN_ERR_NOT_NUMBER, argv[0], w.woptarg);
|
||||||
|
return STATUS_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
opts->fields.push_back(field);
|
||||||
|
}
|
||||||
|
return STATUS_CMD_OK;
|
||||||
}
|
}
|
||||||
string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
||||||
return STATUS_INVALID_ARGS;
|
return STATUS_INVALID_ARGS;
|
||||||
@ -423,6 +439,7 @@ static wcstring construct_short_opts(options_t *opts) { //!OCLINT(high npath co
|
|||||||
if (opts->end_valid) short_opts.append(L"e:");
|
if (opts->end_valid) short_opts.append(L"e:");
|
||||||
if (opts->no_empty_valid) short_opts.append(L"n");
|
if (opts->no_empty_valid) short_opts.append(L"n");
|
||||||
if (opts->no_trim_newlines_valid) short_opts.append(L"N");
|
if (opts->no_trim_newlines_valid) short_opts.append(L"N");
|
||||||
|
if (opts->fields_valid) short_opts.append(L"f:");
|
||||||
return short_opts;
|
return short_opts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -450,6 +467,7 @@ static const struct woption long_options[] = {{L"all", no_argument, nullptr, 'a'
|
|||||||
{L"start", required_argument, nullptr, 's'},
|
{L"start", required_argument, nullptr, 's'},
|
||||||
{L"style", required_argument, nullptr, 1},
|
{L"style", required_argument, nullptr, 1},
|
||||||
{L"no-trim-newlines", no_argument, nullptr, 'N'},
|
{L"no-trim-newlines", no_argument, nullptr, 'N'},
|
||||||
|
{L"fields", required_argument, nullptr, 'f'},
|
||||||
{nullptr, 0, nullptr, 0}};
|
{nullptr, 0, nullptr, 0}};
|
||||||
|
|
||||||
static const std::unordered_map<char, decltype(*handle_flag_N)> flag_to_function = {
|
static const std::unordered_map<char, decltype(*handle_flag_N)> flag_to_function = {
|
||||||
@ -1083,6 +1101,7 @@ static int string_split_maybe0(parser_t &parser, io_streams_t &streams, int argc
|
|||||||
opts.max_valid = true;
|
opts.max_valid = true;
|
||||||
opts.max = LONG_MAX;
|
opts.max = LONG_MAX;
|
||||||
opts.no_empty_valid = true;
|
opts.no_empty_valid = true;
|
||||||
|
opts.fields_valid = true;
|
||||||
int optind;
|
int optind;
|
||||||
int retval = parse_opts(&opts, &optind, is_split0 ? 0 : 1, argc, argv, parser, streams);
|
int retval = parse_opts(&opts, &optind, is_split0 ? 0 : 1, argc, argv, parser, streams);
|
||||||
if (retval != STATUS_CMD_OK) return retval;
|
if (retval != STATUS_CMD_OK) return retval;
|
||||||
@ -1121,8 +1140,17 @@ static int string_split_maybe0(parser_t &parser, io_streams_t &streams, int argc
|
|||||||
if (splits.back().empty()) splits.pop_back();
|
if (splits.back().empty()) splits.pop_back();
|
||||||
}
|
}
|
||||||
auto &buff = streams.out.buffer();
|
auto &buff = streams.out.buffer();
|
||||||
for (const wcstring &split : splits) {
|
if (opts.fields.size() > 0) {
|
||||||
buff.append(split, separation_type_t::explicitly);
|
for (const auto &field : opts.fields) {
|
||||||
|
// field indexing starts from 1
|
||||||
|
if (field - 1 < (long)split_count) {
|
||||||
|
buff.append(splits.at(field - 1), separation_type_t::explicitly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const wcstring &split : splits) {
|
||||||
|
buff.append(split, separation_type_t::explicitly);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +88,16 @@ string split "" abc
|
|||||||
# CHECK: b
|
# CHECK: b
|
||||||
# CHECK: c
|
# CHECK: c
|
||||||
|
|
||||||
|
string split --fields=2 "" abc
|
||||||
|
# CHECK: b
|
||||||
|
|
||||||
|
string split --fields=2,3 "" abc
|
||||||
|
# CHECK: b
|
||||||
|
# CHECK: c
|
||||||
|
|
||||||
|
string split --fields=2,9 "" abc
|
||||||
|
# CHECK: b
|
||||||
|
|
||||||
seq 3 | string join ...
|
seq 3 | string join ...
|
||||||
# CHECK: 1...2...3
|
# CHECK: 1...2...3
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user