【问题标题】:cd to a folder name starting with tilde (~) in bash scriptcd 到 bash 脚本中以波浪号 (~) 开头的文件夹名称
【发布时间】:2014-08-31 17:29:09
【问题描述】:

我正在尝试创建一个非常简单的 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"

我输入的文件夹路径是

~/Dropbox/我的项目

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

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

谁能指出我做错了什么?

【问题讨论】:

  • 也许这可以解决你的问题:stackoverflow.com/questions/5748216/…
  • cd 有必要吗?如果您只想删除该文件,您可以在没有它的情况下执行此操作。您仍然需要强制 bash 扩展 ~,可能使用 eval。更改目录后,rm -f readme.md 就足够了。此外,您可以使用read -p "prompt:" 来避免单独的echo

标签: macos bash shell unix


【解决方案1】:

波浪号 (~) 仅在直接出现(且未引用)时才会扩展到主目录。使用cd "$DESTINATION_FOLDER" 时,$DESTINATION_FOLDER 中的字符按字面意思使用,包括开头的~。即使您使用了cd $DESTINATION_FOLDER~ 也会被直接插入,尽管$DESTINATION_FOLDER 中的空格和全局元字符会被扩展。所以实际上没有办法对变量中的值进行波浪号扩展。 [注1]

有些人会建议使用eval,但你必须非常小心。如果$DESTINATION_FOLDER 包含shell 元字符,eval cd "$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 是一个 glob,而不是正则表达式。

编辑:在评论中,Tom Fenech 建议如果可以处理 ~user 以及仅处理 ~ 会很酷。同样,处理 ~+2 [注 2] 之类的 bashism 会更酷。

确实如此。不幸的是,bash 没有提供进行波浪号扩展的内置函数。或任何其他类型的扩展。所以我认为有必要使用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 应谨慎对待,因此请在部署前仔细检查。)


注意事项:

  1. 一般来说,这无关紧要,因为大多数脚本将输入作为参数,而不是从控制台读取它们。这样的脚本可以写成如下:

    cdrm() {
      cd "$1" && rm -rf readme.md
    }
    

    那么您就可以使用波浪号调用它:

    cdrm ~/Dropbox/myproject/
    

    因为~会在cdrm的调用中展开,所以$1会有展开后的值。

  2. 我从未使用过~+N 波浪号扩展,但可能有人认为它们必不可少

【讨论】:

  • 关于扩展的有趣想法,如果它也适用于~user,那将是锦上添花。您在第二段中所说的对我不起作用 - 尝试使用输入 read a; echo $a ~
  • 您的第二段不正确,变量扩展遵循波浪号扩展,因此使用cd $FOO$FOO 中的波浪号不会被扩展。
  • @user2719058:100% 正确。我的错。修复了,我希望。
  • @TomFenech:是的,我的第二段是错误的。我重写了它。我会给~user 一个想法;这应该是可能的,但我不是 100% 确定如何。
  • @TomFenech:我对添加的解决方案一点也不满意,但它就在那里。
【解决方案2】:

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

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

请参阅this SO question,了解如何在 bash 脚本中扩展波浪号。

【讨论】:

  • 但是您可以使用eval cd "$DESTINATION_FOLDER" 使其工作。
猜你喜欢
  • 2018-12-20
  • 2011-02-08
  • 1970-01-01
  • 2022-06-29
  • 2015-01-04
  • 2014-07-15
  • 2011-06-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多