为什么我无法在 bash 脚本上转义空格?

Luc*_*cio 82 shell quoting

我试图在 Bash 中为路径转义空格字符,但使用反斜杠引号都不起作用。

.sh 脚本:

ROOT="/home/hogar/Documents/files/"
FILE=${ROOT}"bdd.encrypted"
DESTINATION="/home/hogar/Ubuntu\ One/folder"

mv ${FILE} ${DESTINATION}
Run Code Online (Sandbox Code Playgroud)

执行脚本 ( ./file) 后,结果如下:

mv: target 'One/folder' is not a directory
Run Code Online (Sandbox Code Playgroud)

为什么mv命令会拆分字符串,我该如何阻止这种情况发生?

Bra*_*iam 93

您正在扩展 DESTINATION 变量,如果您echo这样做,您将得到:

echo ${DESTINATION}
/home/hogar/Ubuntu\ One/folder
Run Code Online (Sandbox Code Playgroud)

mv不明白这一点:

mv ${FILE} ${DESTINATION}                                                
mv: cannot move '/home/hogar/Documents/files/bdd.encrypted' to '/home/hogar/Ubuntu\\ One/folder': No such file or directory
Run Code Online (Sandbox Code Playgroud)

(出于某种原因,我的 mv 更冗长)

为了防止这种情况,您应该使用引号:

mv "${FILE}" "${DESTINATION}"
Run Code Online (Sandbox Code Playgroud)

如果您不需要扩展(因为您之前已经扩展过),只需使用"$..."就足够了:

mv "$FILE" "$DESTINATION"
Run Code Online (Sandbox Code Playgroud)

  • 此外,如果您始终用双引号引用所有内容,则在空格前不需要反斜杠。 (12认同)
  • `${variable}` 和 `$variable` 在所有方面都是等价的,除非紧跟在变量名中的有效字符之后。 (3认同)

小智 29

您准备变量的步骤“足够接近”并且会起作用,但它确实显示出理解上的弱点(这就是您发布的原因!)

对 DESTINATION 的分配只需是以下两个之一:

DESTINATION="/home/hogar/Ubuntu One/folder"
Run Code Online (Sandbox Code Playgroud)

或者

DESTINATION=/home/hogar/Ubuntu\ One/folder
Run Code Online (Sandbox Code Playgroud)

双引号打开引用(一种具有某种魔力的形式),反斜杠能够引用它后面的单个字符。我个人认为使用双引号的形式更可取,因为它在视觉上更好。

顺便说一句,出于类似的原因,我会进行 FILE 分配,例如:

FILE="${ROOT}bdd.encrypted"
Run Code Online (Sandbox Code Playgroud)

这显示了使用${NAME}而不是简单的非常罕见的场合$NAME- 将变量名称与后面的任何字母分开。事实上,如果你在 ROOT 的定义上没有尾随 /,我会写

FILE="$ROOT/bdd.encrypted"
Run Code Online (Sandbox Code Playgroud)

这看起来更好。它也会偶尔给你带来我不会介绍的好处;让我们只是说根据我的经验,尾部斜杠往往更不受欢迎而不是受欢迎,而且绝对不需要它们。(当涉及到符号链接时,它确实会产生影响,但对于这个已经很大的答案来说,这绝对是太多的细节)。

你的问题的症结实际上发生在mv命令,它应该写成

mv "$FILE" "$DESTINATION"
Run Code Online (Sandbox Code Playgroud)

因为现在,您永远不知道表示路径的变量何时会包含空格。再次注意避免使用大括号进行简单的变量扩展。

出现问题的原因是 shell 用于构建命令行的过程。如果你仔细阅读 shell 手册,它基本上说变量(和其他一些东西)被扩展然后它去寻找空间。当然,在这个过程中还有一些更复杂的,但这是你的问题的要点。

还有一点这是相关的,非常值得了解一下:之间的区别$*$@"$*""$@"。那我来谈谈吧!

如果您有一个可以调用的 shell 脚本:

foo -x "attached is the software" "please read the accompanying manual"
Run Code Online (Sandbox Code Playgroud)

然后foo将看到-x$1,和两个字符串作为$2$3(你应该访问的"$2""$3"显而易见的原因)。但是,如果您想将整个参数集传递给另一个脚本,以下内容将在所有空间上遭受可怕的分裂:

bar $*
Run Code Online (Sandbox Code Playgroud)

和以下(这是非常原始的 Bourn shell 的 0.X 版本中的唯一方法)将导致所有参数作为单个字符串传递(也是错误的)

bar "$*"
Run Code Online (Sandbox Code Playgroud)

因此添加了以下语法作为一个非常特殊的神奇案例:

bar "$@"
Run Code Online (Sandbox Code Playgroud)

它故意将单个成员$*引用为完整的引用字符串,但将它们分开。现在每个人都是赢家。

尝试$@不使用时的其他效果有点有趣,"$@"但您很快就会发现它实际上并不那么有趣......它只是解决一个主要问题的特例。

所有支持数组的现代 shell 在特殊情况下也使用 @:

${arr[*]}
"${arr[*]}"
"${arr[@]}"
Run Code Online (Sandbox Code Playgroud)

其中第一个将在所有空格上拆分,导致不可预测数量的单独单词,第二个将在一个字符串中生成所有数组成员,第三个将非常好地将每个数组成员作为单独的完整引用字符串提供。

享受!


dai*_*isy 14

是的,当您将值分配给时,您转义了空间$DESTINATION

但是当你用 mv 命令使用它时,你没有

mv ${FILE} ${DESTINATION}
Run Code Online (Sandbox Code Playgroud)

改用这个:

mv "${FILE}" "${DESTINATION}"
Run Code Online (Sandbox Code Playgroud)