5

I try to use the bash complete builtin to show different options for a command.

I have problems when an option contains a path like in -F/dev/null.

Currently I'm using

#!/bin/bash

_xyz-completion ()
{
  local cur

  COMPREPLY=()   # Array variable storing the possible completions.
  cur=${COMP_WORDS[COMP_CWORD]}

  case "$cur" in
    -*)
    COMPREPLY=( $( compgen -W "-oOption1 -F/dev/null" -- $cur ) )
    ;;
  esac

  return 0
}

complete -F _xyz-completion -o filenames xyz

If -F was already typed, then a Tab completes it successfully.

But if only - was typed, then a Tab shows

null       -oOption1

But I expect to see

-F/dev/null       -oOption1

I tried already -F\/dev\/null, -F//dev//null, "-F/dev/null" and -F\\\/dev\\\/null

It seems to be only a display problem, as the completion itself works as expected.

I can't see how to appropriate escape the slashes in `-F/dev/null`.


To comment the comments:

1)

Never mind, it's a problem also if -F is replaced by a non-option such as -Q. – Benjamin W.

It's not a problem, that the -F looks like a option for complete itself, as it even fails if I changed it to xOPTION1 xF/dev/null

2)

I'm wondering what compgen -W "-oOption1 -F/dev/null" -- - displays for you.

It displays (as expected)

-oOption1
-F/dev/null

As mentioned, -F completes successfully to -F/dev/null

dbc
  • 104,963
  • 20
  • 228
  • 340
jeb
  • 78,592
  • 17
  • 171
  • 225
  • I don't think the slashes are the problem, but `-F` is a valid option for `compgen`. – Benjamin W. Apr 02 '19 at 14:31
  • 1
    Never mind, it's a problem also if `-F` is replaced by a non-option such as `-Q`. – Benjamin W. Apr 02 '19 at 14:36
  • I'm wondering what `compgen -W "-oOption1 -F/dev/null" -- -` displays for you. – Tanktalus Apr 02 '19 at 14:38
  • @Tanktalus If I just test your command interactively, it shows the expected output, nothing truncated, but when used as in the question, `-F/dev` is truncated. Strange. – Benjamin W. Apr 02 '19 at 14:43
  • 1
    I can confirm that the problem persists if you use `_xyz-completion () { COMPREPLY=(-oOption -F/dev/null); }`. – chepner Apr 02 '19 at 14:43
  • Smells like a bug to me. – Benjamin W. Apr 02 '19 at 14:44
  • A few more observations: everything up to the last slash gets removed; the problem is only triggered if the are multiple completion options. `compgen -W '-x -Fblah/slash1/slash2/slash3'` shows `slash3 -x`; `compgen -W '-Fblah/slash1/slash2/slash3'` completes as expected. – Benjamin W. Apr 02 '19 at 14:58

1 Answers1

3

If you remove the -o filenames option from complete your example works as expected. Which makes some sense as the completions aren't filenames. This is with bash version 5.0.2(1).

So:

#!/bin/bash

_xyz-completion ()
{
  local cur

  COMPREPLY=()   # Array variable storing the possible completions.
  cur=${COMP_WORDS[COMP_CWORD]}

  case "$cur" in
    -*)
    COMPREPLY=( $( compgen -W "-oOption1 -F/dev/null" -- $cur ) )
    ;;
  esac

  return 0
}

complete -F _xyz-completion xyz

It definitely seems like a bug that it would truncate part of the completion when there are slashes. And only when displaying the choices, the actual completion works correctly.

EDIT:

After looking into it a little more, the filenames option is used for escaping strings that could have spaces or other breaking characters. Basically cleaning up file names for the shell. From the Programmable Completion Built-in man page

-o filenames:

Tell Readline that the compspec generates filenames, so it can perform any filename-specific processing (like adding a slash to directory names, quoting special characters, or suppressing trailing spaces). This option is intended to be used with shell functions specified with -F.

Apparently that includes stripping everything before and including the last slash.

EDIT2:

Here's a comment from the readline source that bash uses for file name completion. I got this from the bash repo at https://git.savannah.gnu.org/git/bash.git). The master, so 5.0 patch 3 at time of writing.

./lib/readline/complete.c line 697

/* Return the portion of PATHNAME that should be output when listing
   possible completions.  If we are hacking filename completion, we
   are only interested in the basename, the portion following the
   final slash.  Otherwise, we return what we were passed.  Since
   printing empty strings is not very informative, if we're doing
   filename completion, and the basename is the empty string, we look
   for the previous slash and return the portion following that.  If
   there's no previous slash, we just return what we were passed. */
static char *
printable_part (char *pathname)

For filename completion, it only wants to print the basename, everything after the last slash.

nitram
  • 500
  • 2
  • 5
  • It took me a while to realize this was the cause of the behaviour I was encountering, after I had already removed `-o filenames` from the `complete` command I was using, because of previous state in the shell instance I was sourcing it in – rakslice Jul 21 '22 at 10:44