Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to complete filenames relative to another directory?

This is a follow-up question to the discussion in:

https://bugs.launchpad.net/ubuntu/+source/bash-completion/+bug/1394920

Suppose I have a folder ~/tmp with some files and directories:

$ mkdir a; touch file1.txt; mkdir a/b; mkdir a/c; touch a/d; mkdir a/b/c

I now try to make a completion script to complete filenames in ~/tmp, but the complete -o filenames option is only working correctly if the current directory is ~/tmp.

See the above link for more background information. This is as far as I got:

$ cat setup
_compTest() {
    local cur baseFolder
    cur="${COMP_WORDS[$COMP_CWORD]}"
    baseFolder=~/tmp
    compopt -o nospace
    COMPREPLY=(  $(
       cd "$baseFolder"
       if [[ ${cur: -1} != "/" && -d $cur ]] ; then
           echo "$cur/"
       else
           compgen -f "$cur"
       fi
      )  )
}
complete -F _compTest aaa

Then I source it:

$ . setup

and I can then do

$ aaa <tab><tab>
  • Problem 1 : Slashes are not added at the end of directory names in the completion list ( this is desired to easy separate directories from file names in the completion list)

  • Problem 2 : For aaa a/<tab><tab> the completion list is a/b a/c a/d but the a/ prefix should not be there. It should be b/ c/ d instead.

like image 921
Håkon Hægland Avatar asked Dec 02 '14 07:12

Håkon Hægland


People also ask

How do you write a relative file path?

A relative path refers to a location that is relative to a current directory. Relative paths make use of two special symbols, a dot (.) and a double-dot (..), which translate into the current directory and the parent directory. Double dots are used for moving up in the hierarchy.

What is absolute path of a file?

An absolute path always contains the root element and the complete directory list required to locate the file. For example, /home/sally/statusReport is an absolute path. All of the information needed to locate the file is contained in the path string.

Does file path include filename?

Paths include the root, the filename, or both. That is, paths can be formed by adding either the root, filename, or both, to a directory.

What is an example of a relative path?

A relative path is a way to specify the location of a directory relative to another directory. For example, suppose your documents are in C:\Sample\Documents and your index is in C:\Sample\Index. The absolute path for the documents would be C:\Sample\Documents.

How to change the current working directory of a relative file?

We can change the current working directory with change directory function chdir () Relative file path is always going to be relative with current working directory, it won't contain root folder. Let's say there are two folders 'one' and 'two' in my F drive, and 'one' is the cwd.

Which character separates the file path and filename?

The directory separator character separates the file path and the filename. The following are some examples of UNC paths: The root directory of the C: drive on system07. The Foo.txt file in the Test directory of the \\Server2\Share volume.

What are relative filenames and how are they used?

Relative filenames describe files that are referenced relative to an assumed position in the file system. In Figure 3.3, even Personnel/Taylor,D. isn’t unique because that can be found in both Indiana and Washington.

What is the relative file path in Linux?

Relative file path is always going to be relative with current working directory, it won't contain root folder. Let's say there are two folders 'one' and 'two' in my F drive, and 'one' is the cwd.


2 Answers

I would write this function as:

_compTest () 
{ 
    local cur; local tmp;  local tmp_escaped; local i;
    _get_comp_words_by_ref cur;
    local _compreply=()
    tmp=~/tmp/
    tmp_escaped=${tmp//\//\\\/}
    cur=$tmp$cur;

    if [ "$1" == "-d" ]; then
        _cd
    else
        _filedir;
    fi;
    for i in "${COMPREPLY[@]}"; do
        [ -d "$i" ] && [ "$i" != "$tmp." ] && [ "$i" != "$tmp.." ] && i="$i/"
        _compreply=("${_compreply[@]}" "$i")
    done

    COMPREPLY=(${_compreply[@]/$tmp_escaped/})
} && complete -o nospace -F _compTest aaa_files

_compTestDir()
{
    _compTest -d
} && complete -o nospace -F _compTestDir aaa_directories

It has 3 parts,

  1. prefixing the $cur with your base directory - ~/tmp.
  2. Using standard bash completion routine _filedir used for cd/ls etc.
  3. Removing ~/tmp from COMPREPLY

Just for the record: You can use this logic to complete the file names relative to many other types of path, e.g.

  1. I use it to complete perforce paths //....
  2. You can also complete http://localhost/* paths, relative to your public_html directory.
like image 164
anishsane Avatar answered Oct 05 '22 14:10

anishsane


Would have added as a comment, but I don't have the reputation...

Building on @anishsane's answer, if there is only a single completion option and it is not a directory, then you may not want to include the nospace option. For example, the program may accept another argument after the one you are currently completing. If there is only one option, we should accept that option and move on to the next arg.

To do this, remove nospace from the complete line and add logic to enable nospace only when needed:

    # [...]

    # Do not include trailing space in results if there is more than one option
    # or if the only option is a directory
    [ "${#_compreply[@]}" -gt 1 ] && compopt -o nospace
    [ "${#_compreply[@]}" -eq 1 ] && [ -d "${_compreply[0]}" ] && compopt -o nospace

    COMPREPLY=(${_compreply[@]/$tmp_escaped/})
} && complete -F _compTest aaa_files
like image 44
duick Avatar answered Oct 05 '22 16:10

duick