diff --git a/bash_completion b/bash_completion index 8ab042a019b..2f00f7bd8d7 100644 --- a/bash_completion +++ b/bash_completion @@ -1003,7 +1003,7 @@ _init_completion() done COMPREPLY=() - local redir="@(?([0-9])<|?([0-9&])>?(>)|>&)" + local redir='@(?(+([0-9])|{[a-zA-Z_]*([a-zA-Z_0-9])})@(>?([>|&])|&])|<?(>))' _get_comp_words_by_ref -n "$exclude<>&" cur prev words cword # Complete variable names. diff --git a/test/t/unit/test_unit_init_completion.py b/test/t/unit/test_unit_init_completion.py index 062b53e8205..cd81b3daa78 100644 --- a/test/t/unit/test_unit_init_completion.py +++ b/test/t/unit/test_unit_init_completion.py @@ -6,7 +6,7 @@ @pytest.mark.bashcomp( cmd=None, ignore_env=r"^[+-](COMP(_(WORDS|CWORD|LINE|POINT)|REPLY)|" - r"cur|cword|words)=", + r"cur|cword|words)=|^\+declare -f _cmd1$", ) class TestUnitInitCompletion(TestUnitBase): def test_1(self, bash): @@ -32,3 +32,28 @@ def test_redirect(self, bash, redirect): bash, "%s " % redirect, cwd="shared/default" ) assert all(x in completion for x in "foo bar".split()) + + @pytest.fixture(scope="class") + def cmd1_empty_completion_setup(self, bash): + assert_bash_exec( + bash, + "_cmd1() { local cur prev words cword; _init_completion; } && " + "complete -F _cmd1 cmd1", + ) + + @pytest.mark.parametrize("redirect", "> >> 2> {fd1}> < &> &>> >|".split()) + def test_redirect_2(self, bash, cmd1_empty_completion_setup, redirect): + # Note: Bash 4.3 and below cannot properly extract the redirection ">|" + if redirect == ">|": + skipif = "((BASH_VERSINFO[0] * 100 + BASH_VERSINFO[1] < 404))" + try: + assert_bash_exec(bash, skipif, want_output=None) + except AssertionError: + pass + else: + pytest.skip(skipif) + + completion = assert_complete( + bash, "cmd1 %s f" % redirect, cwd="shared/default" + ) + assert "foo" in completion