cd到bash脚本中以tilde(〜)开头的文件夹名称

时间:2014-08-31 17:29:09

标签: macos bash shell unix

我试图创建一个非常简单的bash脚本来执行以下操作:

  • 输入用户输入项目文件夹
  • 设置在变量中输入的值
  • cd到输入的文件夹
  • 删除readme.md文件

从我所读过的内容中我得出了这个:

#!/bin/bash

echo "Enter the project folder path:"
read -e DESTINATION_FOLDER

cd "$DESTINATION_FOLDER"
rm -rf "$DESTINATION_FOLDER/readme.md"

我输入的文件夹路径是

  

〜/升降梭箱/ myproject的

我得到的错误是下面的错误。但是该文件夹存在,我可以使用cd命令手动访问它。

./cleanup.sh: line 6: cd: ~/Dropbox/myproject/: No such file or directory

任何人都可以指出我做错了吗?

2 个答案:

答案 0 :(得分:3)

如果直接(并且未引用),波形符()仅扩展到主目录。当您使用cd "$DESTINATION_FOLDER"时,$DESTINATION_FOLDER中的字符将按字面使用,包括前导。即使您使用过cd $DESTINATION_FOLDER,也会直接插入,但$DESTINATION_FOLDER中的空格和glob元字符会被扩展。因此,实际上没有办法对变量中的值进行波浪扩展。 [注1]

有些人会建议使用eval,但你必须非常小心。如果eval cd "$DESTINATION_FOLDER"包含shell元字符,则$DESTINATION_FOLDER会产生意外结果。例如,请考虑以下内容(不要在包含您关注的文件的目录中尝试此操作):

$ mkdir -p ~/tmp/foo && cd ~/tmp/foo
$ touch a few files
$ ls
a  few  files
$ x="~/tmp/foo;rm *"
$ # BEWARE!
$ eval cd "$x"
$ # OOPS!
$ ls
$

正确的方法是自己扩展代字号:

cd "${DESTINATION_FOLDER/#~/$HOME}"

bash中,${var/pattern/replacement}根据$var的值生成一个字符串,其中pattern的第一个实例(如果有)替换为replacement }。如果${name/#pattern/replacement}出现在pattern的开头$var,则表单pattern仅进行替换。 ~user是一个glob,而不是正则表达式。

编辑:在评论中,Tom Fenech建议如果可以处理~以及~+2,那就太酷了。同样,处理像bash [注2]这样的基本原则会更加冷静。

事实上。遗憾的是,eval并未提供内置扩展功能。或任何其他类型的扩张。所以我认为有必要使用eval printf %s,但如上所述,这需要相当谨慎。我认为可以做的最好的事情就是只在不包含元字符的情况下提取波形符前缀,然后只用tilde-expand() { ( shopt -s extglob if [[ $1 =~ ^~[[:alnum:]_.+-]*(/.*)?$ ]]; then printf %s "$(eval printf ${1%%/*})${1##*([^/])}" else printf %s "$1" fi ) } 展开它,如下所示:

eval

(上面没有经过仔细测试。一如既往,cdrm() { cd "$1" && rm -rf readme.md } 应该谨慎对待,所以请在部署之前仔细检查。)


备注:

  1. 通常,这并不重要,因为大多数脚本将输入作为参数而不是从控制台读取它们。这样的脚本可能写成如下:

    cdrm ~/Dropbox/myproject/
    

    然后您可以使用代字号调用它:

    cdrm

    因为会在调用$1时展开,因此~+N会有扩展值。

  2. 我从未使用{{1}}代码扩展,但有些人可能会发现必需

答案 1 :(得分:2)

当您在交互式shell中输入内容时,shell会将~扩展到您的主目录中。它不适用于脚本。

您需要输入完整路径才能使其正常工作。 (当然,它可以是一条相对路径。)

有关如何在bash脚本中扩展代字号,请参阅this SO question