Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Escape slashes in bash complete

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

like image 318
jeb Avatar asked Oct 16 '22 16:10

jeb


1 Answers

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.

like image 140
nitram Avatar answered Oct 30 '22 23:10

nitram