8

I wrote a script called prpsls which creates or edits files inside the directory ~/proposals.

Now I'm writing the autocomplete script for it, and I'm trying to set it up in a way that, after I hit Tab, the complete built-in lists files and directories from the ~/proposals directory, regardless of the current working directory.

The behavior I'm trying to achieve is exactly like if I did complete -f prpsls, but instead of listing files and dirs from the current directory, I want it to always complete to the contents of the ~/proposals directory.

For example, when I write ls and tab, it shows:

$ pwd
/home/user/base_directory
$ ls
file_01   file_02    subdir_01/

If I write subd and Tab, it autocompletes to subdir_01. If I tab again, it show the contents of dir_01:

$ ls subdir_01/
file_03    subdir_02/

The behavior I'm looking for is similar:

$ pwd
/home/user/any/directory/it/should/not/matter
$ prpsls # Tab here
proposal_01    proposal_02    job_01/
$ prpsls job_01/ # Tab again
proposal_03    proposal_draft

My question is: Is it possible to achieve this behavior with the complete built-in? If not, how can I achieve this behavior?

I was hoping I could tell to complete what directory to use as base directory through a flag, but the only flag I see related to directories is -d which is the same as -A directory, which generates a list of directories.

Funnily enough, if I use compgen -f "~/proposals/", it does list files and directories in ~/proposals:

$ pwd
/home/user/any/directory/it/should/not/matter
$ compgen -f "~/proposals/"
~/proposals/job_01
~/proposals/proposal_01
~/proposals/proposal_02

I have seen this question, but:

  • The accepted answer does not work for nested directories. (i.e. Tab and see the content of a directory)
  • The CDPATH answer adds more clutter to other commands.
  • The compgen answer doesn't work for nested directories either.

2 Answers 2

2

Based on this terrific answer I concocted this completion for command foo, which completes files and directories recursively in ~/foodir:

FOO_DIR=$HOME/foodir/

_foo_compgen_filenames() {
    local cur="$1"

    # Files, excluding directories:
    grep -v -F -f <(compgen -d -P ^ -S '$' -- $FOO_DIR"$cur") \
        <(compgen -f -P ^ -S '$' -- $FOO_DIR"$cur") |
        sed -e 's|^\^'$FOO_DIR'||' -e 's/\$$/ /'

    # Directories:
    compgen -d -S / -- $FOO_DIR"$cur" | sed -e 's|'$FOO_DIR'||'
}

_foo_complete() {
    local cur=${COMP_WORDS[COMP_CWORD]}
    COMPREPLY=( $(_foo_compgen_filenames "$cur") )
}

complete -o nospace -F _foo_complete foo

The trick lies in having the compgen completion engine complete the full path to the files in FOO_DIR, but then removing the FOO_DIR path from the completion before it is sent to complete. There's also the matter of adding a slash to the end of directory names, which is not a given and compgen does not have a native way to return files only (but does have a way to return directories only).

0

I tried and found some ways and ideas as to how it can be fixed.

A professional could improve upon my way and create a better/perfect solution.

My system is a Debian GNU/Linux 10 (buster) 4.19.0-16-amd64 with KDE and GNOME

PART I :

  1. Start a new shell

  2. Create the working directory /home/user/ComBash and cd into it

  3. Create a script mytab we will bind this script to the complete command

    Now mytab looks like:

    #!/bin/bash
    echo "MYTAB"
    
  4. chmod +x mytab

  5. Create a second script px for the complete command

    Now px looks like:

    #/usr/bin/env bash
    echo "PX"
    
  6. chmod +x px

  7. Now we start our px script with

    source px
    

    or

    ./px
    

    or

    . ./px
    

    The output should be "PX" (it works)

  8. Create a new folder named bin/ in /home/user/ComBash

    mkdir bin
    

    our files now are

    mytab        | script
    px           | script
    bin/         | folder
    
  9. Now install mytab in the bin/ path of our workdir | /home/user/ComBash/bin and delete mytab in /home/user/ComBash/

    install mytab bin/mytab && rm mytab
    
  10. Now add the workdir bin/ folder to $PATH

    PATH="/home/user/ComBash/bin:${PATH}"
    

    or

    PATH="${PATH}:/home/user/ComBash/bin"
    

    check with

    echo $PATH 
    
  11. Check if mytab works:

    • myt + TAB and press Enter
    • mytab press Enter
    • The output is "MYTAB" (it works)
  12. Create some folders, subfolders and empty files inside the folders, we need this folders and files to check if TAB + TAB works and check what TAB|TAB shows

Now we have our structure for PART II this solution works only in the current shell

If we close and start a new shell, the /home/user/ComBash/bin in $PATH is deleted and the bash can't find the mytab script in /home/user/ComBash/bin, then you have to repeat step 9. (set $PATH) every time you start a new shell.

You can permanently add /home/user/ComBash/bin to $PATH if you don't want to repeat step 9 every time you start the shell.

PART II:

  1. Open and edit the px file

    I try a lot of this commands too but I will not explain them now, you can try and see the result just uncomment and edit the line you want to try.

    Now px looks like:

    #/usr/bin/env bash
    #complete -f mytab
    #complete -D -F "/home/user/ComBash" mytab
    #complete -W "$(find /home/user/ComBash/ -path "" -prune -o \( -type d -ls \) -o \( -type f -ls \) | cut -d/ -f 5-)" mytab
    #complete -W "$(find /home/user/ComBash/ -path "" -prune -o \( -type d -ls \) -o \( -type f -ls \) | cut -d/ -f 2- | sed 's/$/\//')" mytab
    #complete -W "$(find /home/user/ComBash/ -path "" -prune -o \( -ls \)))" mytab
    #complete -W "$(ls -R ${MYPATH} | grep /)" mytab
    MYPATH="/home/user/ComBash/"
    complete -W "$(ls -R ${MYPATH})" mytab
    

    We bound the complete command to the mytab script

    -W means we give complete a list of words

    We get the list of words with ls -R.

    ls -R list all files, subfiles, directories and subdirectories in a given path

  2. We start the script px with

    source px
    

    Every time we do changes inside the script we have to restart the script with ( see step 6. in PART I)

    source px 
    
  3. Now in the shell we use

    mytab + TAB

    and we see the list of all files, subfiles, directories and subdirectories in the given path (/home/user/ComBash/)

    We can select every files, subfiles, directories and subdirectoris and autocomplete with [TAB]

    Select a subfolder and [TAB] that works

    If we press Enter the mytab script will echo "MYTAB"

  4. Lets edit the mytab script in /home/user/ComBash/bin.

    Now mytab looks like:

    #!/bin/bash
    MYPATH=/home/user/ComBash
    # echo $MYPATH
    echo $1
    DATA=$(find ${MYPATH} -name ${1})
    echo $DATA
     ####### DO SOMETHING #######
    

    $1 outputs the folder or file we receive from the px script DATA search and outputs the PATH of the folder or file

Now we can do something with the current file or folder

mytab + Tab press Enter

2
  • Is this a question or an answer...? It can not be both. If you have an new or related question you must ask it as a new question, not in an answer, that's not how SE works. Also, please take the time to code format your answer, and fix the indentation, to make it more readable. Commented Feb 27, 2022 at 16:45
  • 1
    This is an answer, that works with with some restrictions. And thx for editing the code to be more readable.
    – Z0OM
    Commented Feb 27, 2022 at 17:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .