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

我有一个无法解决 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 命令正确:

  1. Ruby 对 heredoc <<-SHELL 字符串进行字符串插值。
  2. 执行脚本的 shell 会进行自己的插值。
  3. 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/\\/ /' ...

看起来不错!

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

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

现在我们可以正确地组装我们的脚本了:

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

相似文章

随机推荐

最新文章