0

I have some directories with music files

$ tree -d ~/Music/
/home/boffi/Music/
├── Aldous_Harding
│   ├── Aldous_Harding
│   ├── Designer
│   ├── Party
│   └── Warm_Chris
├── Madness
│   └── The_Very_Best
└── Mitski
    ├── Be_the_Cowboy
    ├── Bury_Me_At_Makeout_Creek
    ├── Laurel_Hell
    ├── Lush
    ├── Puberty_2
    ├── Retired_from_Sad,_New_Career_in_Business
    └── The_Land_Is_Inhospitable_and_So_Are_We

and a script

$ cat ~/bin/pmd
# Play Music Directories

root=/home/boffi/Music/
list_of_dirs=""
for dir in $@ ; do
  list_of_dirs=${list_of_dirs}" ${root}$dir"
done
list_of_files=$( find ${list_of_dirs} | shuf )
echo mpv -no-terminal -no-video ${list_of_files}
$

When I cd ~/Music and use completions, then my command works as intended; when I execute it from a generic directory it does not work, as the completion (at its best) places the full path names on the command line.

How can I convince bash/readline to complete on directories/subdirectories rooted in ~/Music, without placing the full path name on the command line?


I have read autocomplete command from a certain folder and Bash autocomplete: List files and directories from other directory.

In the first case, the command take a single argument, in the second case the full path name is placed on the command line and it cannot complete on subdirectories.

Following a suggestion in a comment, I've implemented what is suggested here, but completing after pmds would list all the executables in my system, and if I execute the command anyway, I'm teletransported to ~/Music

$ pwd
/home/boffi
$ tail -3 .bashrc
alias yt="yt-dlp -f 'bestvideo[height<=?720]+bestaudio/best' -write-sub --write-auto-sub --sub-lang 'en.*' -o"

. /home/boffi/script
$ cat script
#!/usr/bin/bash

pmds () { echo /home/boffi/Music/$1 ; }
goM ()  { cd /home/boffi/Music ; }
complete -s -F goM pmds

$ . .bashrc
$ pmds <TAB>
Display all 6241 possibilities? (y or n)
$ pmds pippo
/home/boffi/Music/pippo
$ pwd
/home/boffi/Music
$ 
2
  • There's nothing in unix.stackexchange.com/a/630139/70524 that restricts it to one argument. Using the example there go one-arg <tab> works just fine. If you did try it and could only complete one argument, show us the actual code you used.
    – muru
    Feb 20 at 11:02
  • @muru I tried to follow your advice, but I probably made a mistake
    – gboffi
    Feb 23 at 20:00

2 Answers 2

1

Experimenting a bit, there's an even simpler way using complete -C. Define a function that writes out the directory names for completion to the output:

_pmd () {
  shopt -s nullglob
  cd /home/boffi/Music
  # $2 is the word being completed
  printf "%s\n" "$2"*/   # The trailing `/` restricts matches to directories.
}

Then:

complete -C _pmd -o filenames pmd

-C runs the _pmd in a subshell, so we don't need to restore the working directory (a problem with the solution in this answer - which didn't affect that user since their command changed the directory in the end anyway). Nor do we need to worry about resetting nullglob. Using -o filenames also allows readline to quote the completion appropriately if you've directory entries with special characters in them. While a subshell is used, everything else here is built-in to the shell, so we save the cost of forking and running an external command.

0

I realized it's simpler... In the end I added a completion function to my ~/.bashrc and binded it to my command name

$ tail .bashrc

_pmd()
{   local cur opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    opts=$( cd /home/boffi/Music/ ; find * -type d | sed 's.$./.' )
    COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
}
complete -F _pmd pmd

$ pmd <TAB>
Aldous_Harding/
Aldous_Harding/Aldous_Harding/
Aldous_Harding/Designer/
Aldous_Harding/Party/
Aldous_Harding/Warm_Chris/
Madness/
Madness/The_Very_Best/
Mitski/
Mitski/Be_the_Cowboy/
Mitski/Bury_Me_At_Makeout_Creek/
Mitski/Laurel_Hell/
Mitski/Lush/
Mitski/Puberty_2/
Mitski/Retired_from_Sad,_New_Career_in_Business/
Mitski/The_Land_Is_Inhospitable_and_So_Are_We/
$ pmd 

You must log in to answer this question.

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