Vagrant 机器无法 运行 正确使用 - 如何在 Vagrant shell 脚本中转义反斜杠

Vagrant machine wont run SED correctly - How to escape backslashes in Vagrant shell scripts

我有一个问题,我无法解决 vagrant provisioning 问题。我有一个包含反斜杠的文件,我需要将其删除,但 Vagrant (Ruby?) 不会这样做,我已经尝试了 2 天了。

Vagrant.configure("2") do |config|  
%w{test2 }.each_with_index do |name, i|
config.vm.define name do |node|
 node.vm.provider "virtualbox" do |vb|
   vb.name = "test#{i + 2}"
   vb.memory = 512
   vb.cpus = 1
 end
 node.vm.box = "ubuntu/focal64"
 node.disksize.size = "1GB"
 node.vm.hostname = name
 node.vm.network :private_network, ip: "10.0.5.#{i + 12}"
 node.vm.provision :shell, privileged: false, inline: <<-SHELL       
   cat /vagrant/control_join_file.sh | tr -d '\n' > /vagrant/control_join_file#{i + 12}.sh       
   sed -i -E "s@\@ @" /vagrant/control_join_file#{i + 12}.sh
   chmod +x /vagrant/control_join_file#{i + 12}.sh 
   sudo /vagrant/control_join_file#{i + 12}.sh 
 SHELL
end
end      
end

下面这行是罪魁祸首,如果我把它注释掉就会发生。

sed -i -E "s@\@ @" /vagrant/control_join_file#{i + 12}.sh

我将分隔符更改为 @ 符号并尝试了数百种方法。我真的希望有人能指出这里的错误。

此外,如果我 运行 它喜欢这样它就可以工作。我可以查看创建的文件,果然 4 已更改为百分号。问题似乎与反斜杠有关。

sed -i -E "s@4@%@" /vagrant/control_join_file#{i + 12}.sh

事实证明这个问题比你想象的要复杂。问题是你在这里进行了三个字符串插值阶段。每个阶段都需要正确转义字符串和反斜杠,以便最终 sed 命令正确:

  1. Ruby 对 heredoc <<-SHELL 字符串进行字符串插值。
  2. 执行脚本的shell自己插值
  3. Sed 对您提供的正则表达式进行自己的插值。

让我们从第 3 阶段开始倒推,以便我们在开始其他阶段之前知道最终要求是什么。

第 3 阶段 - Sed

我们知道 sed 要求在其正则表达式中对反斜杠进行转义。所以sed命令需要下面的字符串s/\/ /.

然后问题就变成了:我们如何将那个字符串准确地提供给 sed?

第 2 阶段 - Shell

我们知道我们必须向 sed 提供两个反斜杠。让我们 运行 在 shell 中进行一些测试,看看它如何转义反斜杠:

$ echo "\"
\

啊哈。结果是 shell (bash) 在双引号内插入了反斜杠。单引号呢?

$ echo '\'
\

嗯,这很有用。这意味着我们可以使用单引号来防止 Bash.

中的反斜杠插值

我们来测试一下:

$ echo '\' | sed -E 's/\/X/'
X\

这似乎有效。注意,如果要替换所有反斜杠,需要使用g修饰符:

$ echo '\' | sed -E 's/\/X/g'
XX

好的。看来我们已经收集了进入阶段 1 的所有拼图。

阶段 1 - Ruby

在 Ruby 中,heredoc <<-SHELL 运算符进行自己的字符串插值。因此需要转义反斜杠。

让我们在irb中测试一下:

> puts <<-SHELL
sed -E 's/\\/ /' ...
SHELL

sed -E 's/\/ /' ...

看起来不错!

到目前为止我们学到了什么:

  1. Ruby heredoc <<-SHELL 要求转义反斜杠。
  2. shell(Bash 或 sh),需要反斜杠进行转义,除非使用单引号.
  3. Sed 需要两个反斜杠。

现在我们可以 assemble 我们的脚本正确了:

node.vm.provision :shell, privileged: false, inline: <<-SHELL
  ...
  sed -i -E 's/\\/ /' /vagrant/control_join_file#{i + 12}.sh
  ...
SHELL