diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 037c4bbd8..1320c9698 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -36,6 +36,8 @@ const wchar_t *tokenizer_get_error_message(tokenizer_error_t err) { return _(L"Invalid input/output redirection"); case tokenizer_error_t::invalid_pipe: return _(L"Cannot use stdin (fd 0) as pipe output"); + case tokenizer_error_t::invalid_pipe_ampersand: + return _(L"|& is not valid. In fish, use &| to pipe both stdout and stderr."); case tokenizer_error_t::closing_unopened_subshell: return _(L"Unexpected ')' for unopened parenthesis"); case tokenizer_error_t::illegal_slice: @@ -570,6 +572,10 @@ maybe_t tokenizer_t::next() { result->offset = start_pos; result->length = 2; this->buff += 2; + } else if (this->buff[1] == L'&') { + // |& is a bashism; in fish it's &|. + return this->call_error(tokenizer_error_t::invalid_pipe_ampersand, this->buff, + this->buff); } else { auto pipe = pipe_or_redir_t::from_string(buff); assert(pipe.has_value() && pipe->is_pipe && diff --git a/src/tokenizer.h b/src/tokenizer.h index a24a26d38..571d20c01 100644 --- a/src/tokenizer.h +++ b/src/tokenizer.h @@ -51,6 +51,7 @@ enum class tokenizer_error_t { unterminated_escape, invalid_redirect, invalid_pipe, + invalid_pipe_ampersand, closing_unopened_subshell, illegal_slice, closing_unopened_brace, diff --git a/tests/checks/redirect.fish b/tests/checks/redirect.fish index d7768c70a..61c9eb004 100644 --- a/tests/checks/redirect.fish +++ b/tests/checks/redirect.fish @@ -24,4 +24,9 @@ cat $tmpdir/file.txt echo noclobber &>>? $tmpdir/file.txt #CHECKERR: {{.*}} The file {{.*}} already exists +eval "echo foo |& false" +#CHECKERR: {{.*}} |& is not valid. In fish, use &| to pipe both stdout and stderr. +#CHECKERR: echo foo |& false +#CHECKERR: ^ + rm -Rf $tmpdir