Shell Script thinks directory does not exist when running cd

982 Views Asked by At

I have a shell script (which I source into .bashrc) that allows me to jump to my projects directory from anywhere.

cdp(){
  proj="~/dev/projects/$@/"
  builtin cd $proj
}

_my_cdp()
{
  local cur opts
      cur="${COMP_WORDS[COMP_CWORD]}"
      opts=$(ls ~/dev/projects/)
      COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
}

complete -o nospace -F _my_cdp cdp

The problem is, the cd on line 3 says:

bash: cd: ~/dev/projects/jsonparse/: No such file or directory

Here is the full console output showing the error and proving the directory exists.

amflare:~$ cd dev/projects/ (go to dir)
amflare:~/dev/projects$ ls -al (look at contents)
total 68
drwxrwxr-x 17 www-data amflare 4096 Dec 29 13:11 .
drwxrwxr-x  4 amflare amflare 4096 Dec  6 17:32 ..
drwxrwxr-x  3 www-data amflare 4096 Dec 22 17:33 bot
drwxrwxr-x  3 www-data amflare 4096 Dec 20 15:17 jsonparse
drwxrwxr-x  3 www-data amflare 4096 Dec 28 19:58 magic
drwxrwxr-x  2 www-data amflare 4096 Nov 11 14:42 test
amflare:~/dev/projects$ cd (go to home)
amflare:~$ cdp (run autocomplete)
bot         jsonparse   magic       test
amflare:~$ cdp jsonparse (pick target)
bash: cd: ~/dev/projects/jsonparse/: No such file or directory
amflare:~$ (still in home)

I've tried everything I can think of and a few things google gave back for other distros (I'm on Ubuntu Gnome 16.04). No matter what I do, the shell script will not acknowledge the existence of anything inside ~/dev/projects/. Additionally, it does not work when I have the functions in .bashrc itself, so I don't think it is a subshell problem. Please let me know if you need any more information. Thanks.

1

There are 1 best solutions below

4
On BEST ANSWER

That's because the tilde isn't expanded. Use proj=~/"dev/projects/$@/" with the tilde out of the quotes. Or use the variable HOME: proj="$HOME/dev/projects/$@". Note, though, that using $@ is not really good. What do you want if there are several arguments?


Also, don't use opts=$(ls ~/dev/projects/). Use compgen directly like so:

cdp() {
  local proj=~/dev/projects/
  builtin cd "$proj$1"
}

_my_cdp() {
    local proj=~/dev/projects/
    local i p
    COMPREPLY=()
    while IFS= read -r i; do
        printf -v p '%q' "${i#"$proj"}"
        COMPREPLY+=( "$p" )
    done < <(compgen -d -- "$proj$2")
}

When completing, the completion function _my_cdp will have as parameter $2 the word being completed; we give that to compgen -d (with the prefix $proj), and loop through the results (the results are output one per line—so yeah, it'll break if you have newlines in your directories). With each name found, we quote it using printf '%q', while using a parameter expansion to remove the prefix $proj. The quoting is used so as not to break if directory name contains spaces or funny characters (e.g., glob characters), and we populate the array COMPREPLY one term at a time. That's a rather robust way to deal with completion (yet not 100% unbreakable, but it'll do the job for your non-crazy directory names!).