diff --git a/bash_completion b/bash_completion
index e3f9ebcc67b..0e9fd8f4a13 100644
--- a/bash_completion
+++ b/bash_completion
@@ -1289,11 +1289,14 @@ _comp_quote_compgen()
# Ignored with `-d`.
# OPTIONS
# -d Complete only on directories
-
# -f Perform `compopt -o filenames` modifications manually. This
# suffixes a slash to a directory name. When "-C
" or "-P
# " is specified to the "_comp_compgen" call, this option
# is automatically turned on.
+# -X PATTERN
+# @since 2.18 Specify the filename pattern to exclude, or the
+# filename pattern to include prefixed by "!". When this option is
+# specified, $1 should be empty or not specified.
# @return 0 if at least one completion is generated, or 1 otherwise.
#
# @since 2.12
@@ -1302,7 +1305,7 @@ _comp_compgen_filedir()
_comp_compgen_tilde && return
local -a toks
- local _dir="" _filenames=""
+ local _dir="" _filenames="" _xspec=""
if [[ ${_comp_compgen__dir-} || ${_comp_compgen__prefix-} ]]; then
# When "-C " or "-P " is specified, the working directory
# does not contain the generated candidates as filenames. In the case
@@ -1317,10 +1320,11 @@ _comp_compgen_filedir()
fi
local OPTIND=1 OPTARG="" OPTERR=0 _opt
- while getopts ":df" _opt "$@"; do
+ while getopts ":dfX:" _opt "$@"; do
case $_opt in
d) _dir=set ;;
f) _filenames=set ;;
+ X) _xspec=$OPTARG ;;
*)
printf "bash_completion: %s: usage error\n" "$FUNCNAME" >&2
return 2
@@ -1329,6 +1333,10 @@ _comp_compgen_filedir()
done
shift "$((OPTIND - 1))"
local _arg=${1-}
+ if [[ $_xspec && $_arg ]]; then
+ printf 'bash_completion: %s: "-X xspec" and "$1" cannot be specified simultaneously\n' "$FUNCNAME" >&2
+ return 2
+ fi
if [[ $_dir ]]; then
_comp_compgen -v toks -- -d
@@ -1344,7 +1352,7 @@ _comp_compgen_filedir()
# Munge xspec to contain uppercase version too
# https://lists.gnu.org/archive/html/bug-bash/2010-09/msg00036.html
# news://news.gmane.io/4C940E1C.1010304@case.edu
- local _xspec=${_arg:+"!*.@($_arg|${_arg^^})"}
+ [[ $_xspec ]] || _xspec=${_arg:+"!*.@($_arg|${_arg^^})"}
local _opts=(-f -X "$_xspec")
# Use plusdirs to get dir completions if we have a xspec; if we don't,
@@ -1543,20 +1551,8 @@ _comp_variable_assignments()
case $prev in
TZ)
- cur=/usr/share/zoneinfo/$cur
- _comp_compgen_filedir
- if ((${#COMPREPLY[@]})); then
- for i in "${!COMPREPLY[@]}"; do
- if [[ ${COMPREPLY[i]} == *.tab ]]; then
- unset -v 'COMPREPLY[i]'
- continue
- elif [[ -d ${COMPREPLY[i]} ]]; then
- COMPREPLY[i]+=/
- compopt -o nospace
- fi
- COMPREPLY[i]=${COMPREPLY[i]#/usr/share/zoneinfo/}
- done
- fi
+ _comp_compgen -C /usr/share/zoneinfo filedir -f \
+ -X '@(*.tab|leapseconds|leap-seconds.list|tzdata.zi)'
;;
TERM)
_comp_compgen_terms
diff --git a/completions-core/lvm.bash b/completions-core/lvm.bash
index 5ac6042b263..47003b595d6 100644
--- a/completions-core/lvm.bash
+++ b/completions-core/lvm.bash
@@ -28,11 +28,7 @@ _comp_cmd_lvm__logicalvolumes()
_comp_compgen_split -- "$(lvscan 2>/dev/null |
command sed -n -e "s|^.*'\(.*\)'.*$|\1|p")"
if [[ $cur == /dev/mapper/* ]]; then
- _comp_compgen -a filedir
- local i
- for i in "${!COMPREPLY[@]}"; do
- [[ ${COMPREPLY[i]} == */control ]] && unset -v 'COMPREPLY[i]'
- done
+ _comp_compgen -a filedir -X '*/control'
fi
}
diff --git a/completions-core/ssh.bash b/completions-core/ssh.bash
index 011387902d7..7608270e549 100644
--- a/completions-core/ssh.bash
+++ b/completions-core/ssh.bash
@@ -286,8 +286,7 @@ _comp_xfunc_ssh_compgen_identityfile()
{
local cur=$cur tmp
[[ ! $cur && -d ~/.ssh ]] && cur=~/.ssh/id
- _comp_compgen -v tmp -c "$cur" filedir &&
- _comp_compgen -U tmp -- -W '"${tmp[@]}"' -X "${1:+!}*.pub"
+ _comp_compgen -c "$cur" filedir -X "${1:+!}*.pub"
}
_comp_deprecate_func 2.12 _ssh_identityfile _comp_xfunc_ssh_compgen_identityfile
diff --git a/test/t/unit/test_unit_compgen_filedir.py b/test/t/unit/test_unit_compgen_filedir.py
index 6f6d72751c4..dbac7067d39 100644
--- a/test/t/unit/test_unit_compgen_filedir.py
+++ b/test/t/unit/test_unit_compgen_filedir.py
@@ -37,6 +37,11 @@ def functions(self, request, bash):
"_fcd() { local cur=$(_get_cword); unset -v COMPREPLY; _comp_compgen -C _filedir filedir -d; };"
"complete -F _fcd fcd",
)
+ assert_bash_exec(
+ bash,
+ "_X() { local cur;_comp_get_words cur; unset -v COMPREPLY; _comp_compgen_filedir -X '*.e1'; }; "
+ "complete -F _X X",
+ )
@pytest.fixture(scope="class")
def non_windows_testdir(self, bash, tmp_path_factory):
@@ -312,6 +317,10 @@ def test_29_dotdot(self, bash, functions, funcname):
"..folder/",
]
+ def test_option_X_1(self, bash, functions):
+ completion = assert_complete(bash, "X ", cwd="_filedir/ext")
+ assert completion == sorted("ff.e2 foo/ hh.e2 ii.E1".split())
+
def test_fallback_1(self, bash, functions):
"""Directory names should also be generated by the filedir fallback"""
with bash_env_saved(bash) as bash_env: