Shell脚本在运行cd命令时认为目录不存在

4

我有一个Shell脚本(我将其source到.bashrc中),可以让我从任何地方跳转到我的项目目录。

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

问题在于,第三行的cd命令表示:
bash: cd: ~/dev/projects/jsonparse/: No such file or directory

这是完整的控制台输出,显示了错误信息并证明目录存在。
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)

我已经尝试了我能想到的一切,以及谷歌为其他发行版提供的一些方法(我使用的是Ubuntu Gnome 16.04)。无论我做什么,shell脚本都无法识别~/dev/projects/中的任何内容。此外,当我将函数放在.bashrc中时,它也不起作用,因此我认为这不是子shell问题。如果您需要更多信息,请告诉我。谢谢。


这是因为波浪号没有被扩展。在引号外使用proj=~/"dev/projects/$@/",或者使用变量HOMEproj="$HOME/dev/projects/$@"。但请注意,使用$@并不是很好。如果有多个参数,你想要什么? - gniourf_gniourf
考虑通过 http://shellcheck.net/ 运行此代码--它有很多问题;这可能无法捕获所有问题,但这是一个开始的地方。 - Charles Duffy
请注意,例如 ParsingLs -- ls 不适合于编程使用。而且,缺乏引用会导致其自身的问题。 - Charles Duffy
@CharlesDuffy - 我已经运行过它了,但我想我做错了什么,因为再次运行时出现了问题。感谢您的提醒。 - amflare
1个回答

6
由于波浪号没有被扩展,所以需要将波浪号放在引号外面,使用 proj=~/"dev/projects/$@/"。或者使用变量 HOMEproj="$HOME/dev/projects/$@"。请注意,使用 $@ 并不是一个好习惯。如果有多个参数,您想要什么呢?
此外,请勿使用 opts=$(ls ~/dev/projects/)。直接使用 compgen,如下所示:
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")
}

完成时,完成函数 _my_cdp 将以参数 $2 的形式呈现所要完成的单词;我们将其提供给 compgen -d(使用前缀 $proj),并循环遍历结果(每个结果按行输出,因此如果您的目录中有换行符,则会出现错误)。 对于找到的每个名称,我们使用 printf '%q' 进行引用,并使用参数扩展来删除前缀 $proj。 引用是用来防止目录名包含空格或特殊字符(例如 glob 字符)而导致的错误,并且我们逐个条目地填充数组 COMPREPLY。 这是一种相当强大的完成方式(尽管不是 100% 不容易出错的,但它对于非幸存者的目录名称足够用了!)。

那就是问题所在。谢谢。至于$@,我是基于别人的代码编写的,没有去修改它。不过现在我已经改了。 - amflare
1
@tbirrell:回答稍作修改以获得更好的完成度! - gniourf_gniourf
啊,谢谢!我实际上正在尝试弄清楚这个问题。有很多我不理解的部分。能否麻烦您以代码高尔夫的方式给我讲解一下? - amflare
1
@tbirrell:添加了一个段落;不确定是否都有意义!如果您有任何具体问题,请随时要求一些澄清。 - gniourf_gniourf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接