作为一个正在学习 bash 的人,我一直在编写简单的脚本来测试各种命令。在下面的简单脚本中,我正在尝试使用 read 命令。
#!/bin/bash
read -p "Enter a path to a file: " file_to_copy
cp $file_to_copy /tmp
如果用户在提示时输入测试文件的完整路径,例如 /home/$USER/test,则脚本将按预期执行并在 /tmp 目录中复制“test”。但是,如果用户输入简写 ~/test,终端将返回错误cp:无法统计‘~/test’:没有此文件或目录。为什么输入cp命令时无法找到文件路径的等效表示?
如果我独立在终端输入:
cp ~/test /tmp
测试文件顺利复制,所以可能是我搞砸了变量分配的细微差别,但我无法弄清楚。
谢谢。
答案1
在您的脚本中尝试以下命令:
#!/bin/bash
read -p "Enter a path to a file: " file_to_copy
file_to_copy=$(bash -c "echo $file_to_copy")
file_to_copy=${file_to_copy/~\//$HOME\/}
cp "${file_to_copy}" /tmp
解释:
env
如果存在变量值,第三行将扩展它。~
上述代码的第四行将尝试在您的变量中搜索file_to_copy
,如果找到,它将被$HOME
路径替换。这
\/
是\
的反斜杠转义序列字符/
,因为我们想用~/
替换$HOME/
。
答案2
实际情况是,你实际上是要求 shell 从脚本当前所在的目录中查找~/test
。换句话说,如果你从 运行,它将在名为 的子目录中/home/$USER
寻找文件,而不是扩展名。test
~
/home/$USER/test
您可以使用bash
内置的 eval,它将接受任何参数并将它们组合成一个命令,然后让 shell 运行它。现在,shell 将扩展~
。
xieerqi:
$ ./copyScript.sh
Enter a path to a file: ~/test
Entered /home/xieerqi/test
xieerqi:
$ ls -l /tmp/test
-rw-rw-r-- 1 xieerqi xieerqi 0 Dec 21 01:14 /tmp/test
xieerqi:
$ cat copyScript.sh
#!/bin/bash
read -p "Enter a path to a file: " file_to_copy
file_to_copy=$(eval echo $file_to_copy)
echo "Entered $file_to_copy"
cp "$file_to_copy" /tmp
我建议您也使用引号括住命令替换$( . . .)
,以确保包含空格和特殊字符的文件能够被正确捕获到file_to_copy
变量中
添加
正如评论中所说,eval
内置可能会很危险。
readlink
就我个人而言,我在脚本中经常使用命令,并避免使用~
。既然在这个脚本中你无论如何都会要求用户输入,为什么不使用命令行参数呢?像这样:
xieerqi:
$ ./copyScript.sh testFile.txt
/home/xieerqi/testFile.txt
xieerqi:
$ cat copyScript.sh
#!/bin/bash
FILE="$(readlink -f "$@" )"
echo $FILE
# uncomment for copying
# cp $FILE /tmp
目录名和基名
GNU coreutils 提供了两个命令,basename
和dirname
。basename
显示路径中的最后一个元素,并dirname
显示最后一个元素之前的所有内容。可以使用bash
的内部参数扩展来执行相同操作,但既然我们有这些工具 - 为什么不使用它们:
xieerqi:
$ ./copyScript.sh
Enter path to file: ~/testFile.txt
File entered is /home/xieerqi/testFile.txt
xieerqi:
$ ./copyScript.sh
Enter path to file: ~/sumFile.txt
File entered is /home/xieerqi/sumFile.txt
ERROR, file doesn't exist
xieerqi:
$ cat copyScript.sh
#!/bin/bash
read -p "Enter path to file: " FILEPATH
DIRPATH="$(dirname "$FILEPATH")"
FILENAME="$(basename "$FILEPATH" )"
if [ "$DIRPATH" == "~" ];then
DIRPATH="$HOME"
fi
FILEPATH="$DIRPATH"/"$FILENAME"
echo "File entered is " "$FILEPATH"
[ -e "$FILEPATH" ] || { echo "ERROR, file doesn't exist"; exit 1;}