diff --git a/CHANGELOG.rst b/CHANGELOG.rst index da08aa223..241b134e0 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -23,6 +23,7 @@ Improved terminal support Other improvements ------------------ +- For compatibility with Bash, fish now accepts ``|&`` as alternate spelling of ``&|``, for piping pipe both standard output and standard error (:issue:`11516`). - ``fish_indent`` now preserves comments and newlines immediately preceding a brace block (``{ }``) (:issue:`12505`). For distributors and developers diff --git a/doc_src/language.rst b/doc_src/language.rst index 20d303555..ab05200ae 100644 --- a/doc_src/language.rst +++ b/doc_src/language.rst @@ -235,7 +235,7 @@ It is possible to pipe a different output file descriptor by prepending its FD n will attempt to build ``fish``, and any errors will be shown using the ``less`` pager. [#]_ -As a convenience, the pipe ``&|`` redirects both stdout and stderr to the same process. This is different from bash, which uses ``|&``. +As a convenience, the pipe ``&|`` (as well as the ``|&`` alias which is also supported by Bash) both redirect stdout and stderr to the same process. .. [#] A "pager" here is a program that takes output and "paginates" it. ``less`` doesn't just do pages, it allows arbitrary scrolling (even back!). diff --git a/localization/po/de.po b/localization/po/de.po index f61e2e360..d9d3d2ec4 100644 --- a/localization/po/de.po +++ b/localization/po/de.po @@ -1875,9 +1875,6 @@ msgstr "mit den Parametern '%s'" msgid "with definition" msgstr "mit Definition" -msgid "|& is not valid. In fish, use &| to pipe both stdout and stderr." -msgstr "|& ist ungültig. In fish, nutze &| um stdout und stderr gleichzeitig zu pipen" - #, c-format msgid "…and %u more rows" msgstr "…und %u weitere Zeilen" diff --git a/localization/po/en.po b/localization/po/en.po index d096a26fb..60fcbbb3c 100644 --- a/localization/po/en.po +++ b/localization/po/en.po @@ -1875,9 +1875,6 @@ msgstr "" msgid "with definition" msgstr "" -msgid "|& is not valid. In fish, use &| to pipe both stdout and stderr." -msgstr "" - #, c-format msgid "…and %u more rows" msgstr "…and %u more rows" diff --git a/localization/po/es.po b/localization/po/es.po index 084ef368d..a84769c78 100644 --- a/localization/po/es.po +++ b/localization/po/es.po @@ -1878,9 +1878,6 @@ msgstr "con los argumentos '%s'" msgid "with definition" msgstr "con definición" -msgid "|& is not valid. In fish, use &| to pipe both stdout and stderr." -msgstr "|& no es válido. En fish, usa &| para redirigir stdout y stderr." - #, c-format msgid "…and %u more rows" msgstr "…y %u filas más" diff --git a/localization/po/fr.po b/localization/po/fr.po index e67ba9509..3ac822044 100644 --- a/localization/po/fr.po +++ b/localization/po/fr.po @@ -2004,9 +2004,6 @@ msgstr "avec les arguments « %s »" msgid "with definition" msgstr "avec la définition" -msgid "|& is not valid. In fish, use &| to pipe both stdout and stderr." -msgstr "" - #, c-format msgid "…and %u more rows" msgstr "…et %u lignes de plus" diff --git a/localization/po/ja_JP.po b/localization/po/ja_JP.po index 3ac3d27c2..1df4ba000 100644 --- a/localization/po/ja_JP.po +++ b/localization/po/ja_JP.po @@ -1878,9 +1878,6 @@ msgid "with definition" msgstr "定義されています" # CHK -msgid "|& is not valid. In fish, use &| to pipe both stdout and stderr." -msgstr "|& は無効です。fish では、標準出力と標準エラー出力の両方をパイプするには &| を使用してください" - #, c-format msgid "…and %u more rows" msgstr "…他 %u 行" diff --git a/localization/po/pl.po b/localization/po/pl.po index b3f2fe0de..6944917f3 100644 --- a/localization/po/pl.po +++ b/localization/po/pl.po @@ -1871,9 +1871,6 @@ msgstr "" msgid "with definition" msgstr "" -msgid "|& is not valid. In fish, use &| to pipe both stdout and stderr." -msgstr "" - #, fuzzy, c-format msgid "…and %u more rows" msgstr "…and %u więcej rzędów" diff --git a/localization/po/pt_BR.po b/localization/po/pt_BR.po index 767af82fe..cc873c440 100644 --- a/localization/po/pt_BR.po +++ b/localization/po/pt_BR.po @@ -1876,9 +1876,6 @@ msgstr "" msgid "with definition" msgstr "com definição" -msgid "|& is not valid. In fish, use &| to pipe both stdout and stderr." -msgstr "" - #, fuzzy, c-format msgid "…and %u more rows" msgstr "…e mais %u linhas" diff --git a/localization/po/sv.po b/localization/po/sv.po index 06599416a..429fed65f 100644 --- a/localization/po/sv.po +++ b/localization/po/sv.po @@ -1872,9 +1872,6 @@ msgstr "" msgid "with definition" msgstr "" -msgid "|& is not valid. In fish, use &| to pipe both stdout and stderr." -msgstr "" - #, fuzzy, c-format msgid "…and %u more rows" msgstr "…och %u rader till" diff --git a/localization/po/zh_CN.po b/localization/po/zh_CN.po index 4363ec744..05387746b 100644 --- a/localization/po/zh_CN.po +++ b/localization/po/zh_CN.po @@ -1899,9 +1899,6 @@ msgstr ",参数为 '%s'" msgid "with definition" msgstr ",定义为" -msgid "|& is not valid. In fish, use &| to pipe both stdout and stderr." -msgstr "|& 无效。在 fish 中,用 &| 来同时管道链接 stdout 和 stderr。" - #, fuzzy, c-format msgid "…and %u more rows" msgstr "…还有 %u 行" diff --git a/localization/po/zh_TW.po b/localization/po/zh_TW.po index b19a53a3d..01518bdbb 100644 --- a/localization/po/zh_TW.po +++ b/localization/po/zh_TW.po @@ -1873,9 +1873,6 @@ msgstr ",引數為「%s」" msgid "with definition" msgstr ",定義為" -msgid "|& is not valid. In fish, use &| to pipe both stdout and stderr." -msgstr "|& 無效。在 fish 中請使用 &| 來同時管道傳輸 stdout 和 stderr。" - # 第一個 %s 是刪節號。此字串出現在按下 的清單太長的情況。 #, fuzzy, c-format msgid "…and %u more rows" diff --git a/src/highlight/highlight.rs b/src/highlight/highlight.rs index 613d9376b..7f5e48619 100644 --- a/src/highlight/highlight.rs +++ b/src/highlight/highlight.rs @@ -1733,7 +1733,7 @@ macro_rules! validate { ("VAR", fg(HighlightRole::param)), ("=", fg(HighlightRole::operat), ns), ("false", fg(HighlightRole::command)), - ("|&", fg(HighlightRole::error)), + ("|&", fg(HighlightRole::statement_terminator)), ("true", fg(HighlightRole::command)), ("stuff", fg(HighlightRole::param)), ); diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 14fbb9d36..e3f9e62e3 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -49,7 +49,6 @@ pub enum TokenizerError { UnterminatedEscape, InvalidRedirect, InvalidPipe, - InvalidPipeAmpersand, ClosingUnopenedSubshell, IllegalSlice, ClosingUnopenedBrace, @@ -95,7 +94,7 @@ pub struct PipeOrRedir { pub mode: RedirectionMode, // Whether, in addition to this redirection, stderr should also be dup'd to stdout - // For example &| or &> + // For example &|, |& or &> pub stderr_merge: bool, // Number of characters consumed when parsing the string. @@ -163,9 +162,6 @@ fn from(err: TokenizerError) -> Self { TokenizerError::InvalidPipe => { wgettext!("Cannot use stdin (fd 0) as pipe output") } - TokenizerError::InvalidPipeAmpersand => { - wgettext!("|& is not valid. In fish, use &| to pipe both stdout and stderr.") - } TokenizerError::ClosingUnopenedSubshell => { wgettext!("Unexpected ')' for unopened parenthesis") } @@ -473,10 +469,6 @@ fn next(&mut self) -> Option { self.token_cursor += 2; at_cmd_pos = true; Some(result) - } else if next_char == Some('&') { - // |& is a bashism; in fish it's &|. - Some(self.call_error(TokenizerError::InvalidPipeAmpersand, - self.token_cursor, self.token_cursor, Some(2), 2)) } else { let pipe = PipeOrRedir::try_from(buff). expect("Should always succeed to parse a | pipe"); @@ -1024,6 +1016,7 @@ fn try_from(buff: &wstr) -> Result { ); result.fd = STDOUT_FILENO; result.is_pipe = true; + result.stderr_merge = try_consume(&mut cursor, '&'); } '>' => { consume(&mut cursor, '>'); @@ -1356,6 +1349,9 @@ macro_rules! pipe_or_redir { assert!(pipe_or_redir!("&|").is_pipe); assert!(pipe_or_redir!("&|").stderr_merge); + assert!(pipe_or_redir!("|&").is_pipe); + assert!(pipe_or_redir!("|&").stderr_merge); + assert_eq!(pipe_or_redir!("|&").fd, STDOUT_FILENO); assert!(!pipe_or_redir!("&>").is_pipe); assert!(pipe_or_redir!("&>").stderr_merge); assert!(pipe_or_redir!("&>>").stderr_merge); diff --git a/tests/checks/redirect.fish b/tests/checks/redirect.fish index a111205f5..ef4dddaab 100644 --- a/tests/checks/redirect.fish +++ b/tests/checks/redirect.fish @@ -8,6 +8,9 @@ end outnerr 0 &| count #CHECK: 2 +outnerr 1 |& count +#CHECK: 2 + outnerr appendfd 2>>&1 #CHECK: out appendfd @@ -29,11 +32,6 @@ 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: ^^ - # Ensure that redirection empty data still creates the file. rm -f $tmpdir/file.txt test -f $tmpdir/file.txt && echo "File exists" || echo "File does not exist"