我有一个无法解决 vagrant 配置的问题。我有一个包含反斜杠的文件,我需要删除它,但 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
回答1
事实证明,这个问题比你想象的要复杂。问题是您在这里进行了三个阶段的字符串插值。每个阶段都需要正确转义字符串和反斜杠,以使最终的 sed
命令正确:
- Ruby 对 heredoc
<<-SHELL
字符串进行字符串插值。 - 执行脚本的 shell 会进行自己的插值。
- Sed 对您提供的正则表达式进行自己的插值。
让我们从第 3 阶段开始向后工作,以便在开始其他阶段之前知道最终要求是什么。
阶段 3 - Sed
我们知道 sed 需要在其正则表达式中转义反斜杠。所以 sed 命令需要以下字符串 s/\\/ /
。
那么问题就变成了:我们如何将该字符串准确地提供给 sed?
第 2 阶段 - 贝壳
我们知道我们必须为 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/\\/ /' ...
看起来不错!
到目前为止,我们学到了什么:
- Ruby heredoc
<<-SHELL
需要转义反斜杠。 - shell(Bash 或 sh)要求对反斜杠进行转义,除非使用单引号。
- Sed 需要两个反斜杠。
现在我们可以正确地组装我们的脚本了:
node.vm.provision :shell, privileged: false, inline: <<-SHELL
...
sed -i -E 's/\\\\/ /' /vagrant/control_join_file#{i + 12}.sh
...
SHELL