aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--doc_src/index.hdr.in13
-rw-r--r--src/parse_execution.cpp61
-rw-r--r--src/parse_execution.h7
-rw-r--r--tests/test5.err3
4 files changed, 39 insertions, 45 deletions
diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in
index dd6355db..7f2b30c4 100644
--- a/doc_src/index.hdr.in
+++ b/doc_src/index.hdr.in
@@ -413,8 +413,19 @@ Examples:
- `**` matches any files and directories in the current directory and all of its subdirectories.
-Note that if no matches are found for a specific wildcard, it will expand into zero arguments, i.e. to nothing. If none of the wildcarded arguments sent to a command result in any matches, the command will not be executed. If this happens when using the shell interactively, a warning will also be printed.
+Note that for most commands, if any wildcard fails to expand, the command is not executed, <a href='#variables-status'>`$status`</a> is set to nonzero, and a warning is printed. This behavior is consistent with setting `shopt -s failglob` in bash. There are exactly 3 exceptions, namely <a href="commands.html#set">`set`</a>, <a href="commands.html#count">`count`</a> and <a href="commands.html#for">`for`</a>. Their globs are permitted to expand to zero arguments, as with `shopt -s nullglob` in bash.
+Examples:
+\fish
+ls *.foo
+# Lists the .foo files, or warns if there aren't any.
+
+set foos *.foo
+if test (count $foos) -ge 1
+ ls $foos
+end
+# Lists the .foo files, if any.
+\endfish
\subsection expand-command-substitution Command substitution
diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp
index 0857ec42..f15d0719 100644
--- a/src/parse_execution.cpp
+++ b/src/parse_execution.cpp
@@ -391,7 +391,7 @@ parse_execution_result_t parse_execution_context_t::run_function_statement(const
/* Get arguments */
wcstring_list_t argument_list;
- parse_execution_result_t result = this->determine_arguments(header, &argument_list);
+ parse_execution_result_t result = this->determine_arguments(header, &argument_list, failglob);
if (result == parse_execution_success)
{
@@ -484,7 +484,7 @@ parse_execution_result_t parse_execution_context_t::run_for_statement(const pars
/* Get the contents to iterate over. */
wcstring_list_t argument_sequence;
- parse_execution_result_t ret = this->determine_arguments(header, &argument_sequence);
+ parse_execution_result_t ret = this->determine_arguments(header, &argument_sequence, nullglob);
if (ret != parse_execution_success)
{
return ret;
@@ -611,7 +611,7 @@ parse_execution_result_t parse_execution_context_t::run_switch_statement(const p
/* Expand arguments. A case item list may have a wildcard that fails to expand to anything. We also report case errors, but don't stop execution; i.e. a case item that contains an unexpandable process will report and then fail to match. */
wcstring_list_t case_args;
- parse_execution_result_t case_result = this->determine_arguments(arg_list, &case_args);
+ parse_execution_result_t case_result = this->determine_arguments(arg_list, &case_args, failglob);
if (case_result == parse_execution_success)
{
for (size_t i=0; i < case_args.size(); i++)
@@ -762,34 +762,7 @@ parse_execution_result_t parse_execution_context_t::report_errors(const parse_er
parse_execution_result_t parse_execution_context_t::report_unmatched_wildcard_error(const parse_node_t &unmatched_wildcard)
{
proc_set_last_status(STATUS_UNMATCHED_WILDCARD);
- // unmatched wildcards are only reported in interactive use because scripts have legitimate reasons
- // to want to use wildcards without knowing whether they expand to anything.
- if (get_is_interactive())
- {
- // Check if we're running code that was typed at the commandline.
- // We can't just use `is_block` or the eval level, because `begin; echo *.unmatched; end` would not report
- // the error even though it's run interactively.
- // But any non-interactive use must have at least one function / event handler / source on the stack.
- bool interactive = true;
- for (size_t i = 0, count = parser->block_count(); i < count; ++i)
- {
- switch (parser->block_at_index(i)->type())
- {
- case FUNCTION_CALL:
- case FUNCTION_CALL_NO_SHADOW:
- case EVENT:
- case SOURCE:
- interactive = false;
- break;
- default:
- break;
- }
- }
- if (interactive)
- {
- report_error(unmatched_wildcard, WILDCARD_ERR_MSG, get_source(unmatched_wildcard).c_str());
- }
- }
+ report_error(unmatched_wildcard, WILDCARD_ERR_MSG, get_source(unmatched_wildcard).c_str());
return parse_execution_errored;
}
@@ -865,7 +838,7 @@ parse_execution_result_t parse_execution_context_t::handle_command_not_found(con
wcstring_list_t event_args;
{
- parse_execution_result_t arg_result = this->determine_arguments(statement_node, &event_args);
+ parse_execution_result_t arg_result = this->determine_arguments(statement_node, &event_args, failglob);
if (arg_result != parse_execution_success)
{
@@ -964,8 +937,11 @@ parse_execution_result_t parse_execution_context_t::populate_plain_process(job_t
}
else
{
+ const globspec_t glob_behavior = (cmd == L"set" || cmd == L"count")
+ ? nullglob
+ : failglob;
/* Form the list of arguments. The command is the first argument. TODO: count hack, where we treat 'count --help' as different from 'count $foo' that expands to 'count --help'. fish 1.x never successfully did this, but it tried to! */
- parse_execution_result_t arg_result = this->determine_arguments(statement, &argument_list);
+ parse_execution_result_t arg_result = this->determine_arguments(statement, &argument_list, glob_behavior);
if (arg_result != parse_execution_success)
{
return arg_result;
@@ -992,11 +968,8 @@ parse_execution_result_t parse_execution_context_t::populate_plain_process(job_t
}
/* Determine the list of arguments, expanding stuff. Reports any errors caused by expansion. If we have a wildcard that could not be expanded, report the error and continue. */
-parse_execution_result_t parse_execution_context_t::determine_arguments(const parse_node_t &parent, wcstring_list_t *out_arguments)
+parse_execution_result_t parse_execution_context_t::determine_arguments(const parse_node_t &parent, wcstring_list_t *out_arguments, globspec_t glob_behavior)
{
- /* The ultimate result */
- enum parse_execution_result_t result = parse_execution_success;
-
/* Get all argument nodes underneath the statement. We guess we'll have that many arguments (but may have more or fewer, if there are wildcards involved) */
const parse_node_tree_t::parse_node_list_t argument_nodes = tree.find_nodes(parent, symbol_argument);
out_arguments->reserve(out_arguments->size() + argument_nodes.size());
@@ -1018,16 +991,18 @@ parse_execution_result_t parse_execution_context_t::determine_arguments(const pa
case EXPAND_ERROR:
{
this->report_errors(errors);
- result = parse_execution_errored;
+ return parse_execution_errored;
break;
}
case EXPAND_WILDCARD_NO_MATCH:
{
- // report the unmatched wildcard error but don't stop processing.
- // this will only print an error in interactive mode, though it does set the
- // process status (similar to a command substitution failing)
- report_unmatched_wildcard_error(arg_node);
+ if (glob_behavior == failglob)
+ {
+ // report the unmatched wildcard error and stop processing
+ report_unmatched_wildcard_error(arg_node);
+ return parse_execution_errored;
+ }
break;
}
@@ -1045,7 +1020,7 @@ parse_execution_result_t parse_execution_context_t::determine_arguments(const pa
}
}
- return result;
+ return parse_execution_success;
}
bool parse_execution_context_t::determine_io_chain(const parse_node_t &statement_node, io_chain_t *out_chain)
diff --git a/src/parse_execution.h b/src/parse_execution.h
index 7e1c1f46..a9ce32f5 100644
--- a/src/parse_execution.h
+++ b/src/parse_execution.h
@@ -102,7 +102,12 @@ private:
parse_execution_result_t run_function_statement(const parse_node_t &header, const parse_node_t &block_end_command);
parse_execution_result_t run_begin_statement(const parse_node_t &header, const parse_node_t &contents);
- parse_execution_result_t determine_arguments(const parse_node_t &parent, wcstring_list_t *out_arguments);
+ enum globspec_t
+ {
+ failglob,
+ nullglob
+ };
+ parse_execution_result_t determine_arguments(const parse_node_t &parent, wcstring_list_t *out_arguments, globspec_t glob_behavior);
/* Determines the IO chain. Returns true on success, false on error */
bool determine_io_chain(const parse_node_t &statement, io_chain_t *out_chain);
diff --git a/tests/test5.err b/tests/test5.err
index e69de29b..7dba999c 100644
--- a/tests/test5.err
+++ b/tests/test5.err
@@ -0,0 +1,3 @@
+No matches for wildcard '*ee*'.
+fish: case *ee*
+ ^