bash循环与模式和引号字符


DESTINATION="example_substitute.txt"
echo "$DESTINATION"
DESTINATIONPATH="/home/example/test"
echo "$DESTINATIONPATH"
ARRAY=('EXP1' 'EXP2' 'EXP3 EXP4' 'EXP5 EXP6')
for n in "${ARRAY[@]}"; do
(
echo "$n"
echo "sudo grep -RiIl --exclude-dir={_themes,_styleguide,_templates,_static,_RFCs,_glossary,_scripts,_blockdiag,_Definition} ' $n ' | xargs sed -i 's/ $n / :term:`$n` /g' ./*.rst" >> $DESTINATIONPATH/$DESTINATION
)
done

我想从数组中组合我的grep命令并将其放入$DESTINATIONPATH/$DESTINATION

作为新手echo "$n"带上正确的表情。第二个echo与引号作斗争:

应该是:

sudo grep -RiIl --exclude-dir={_themes,_styleguide,_templates,_static,_RFCs,_glossary,_scripts,_blockdiag,_Definition} ' EXP1 ' | xargs sed -i 's/ EXP1 / :term:`EXP1` /g' ./*.rst
sudo grep -RiIl --exclude-dir={_themes,_styleguide,_templates,_static,_RFCs,_glossary,_scripts,_blockdiag,_Definition} ' EXP2 ' | xargs sed -i 's/ EXP2 / :term:`EXP2` /g' ./*.rst
sudo grep -RiIl --exclude-dir={_themes,_styleguide,_templates,_static,_RFCs,_glossary,_scripts,_blockdiag,_Definition} ' EXP3 EXP4 ' | xargs sed -i 's/ EXP3 EXP4 / :term:`EXP3 EXP4` /g' ./*.rst
sudo grep -RiIl --exclude-dir={_themes,_styleguide,_templates,_static,_RFCs,_glossary,_scripts,_blockdiag,_Definition} ' EXP5 EXP6 ' | xargs sed -i 's/ EXP5 EXP6 / :term:`EXP5 EXP6` /g' ./*.rst

您的问题是运行脚本的shell将对反斜杠( `...` )作为命令替换进行解析,因此shell尝试将其中的值作为命令执行,并用命令的输出替换对反斜杠及其内容。我假设你没有EXP1命令,所以它会引发一个错误,并被空字符串取代,这将破坏你的sed命令在你的输出。

要获得预期的输出,您应该简单地转义反引号(当在双引号字符串中找到单引号时,不需要对单引号做任何操作):

echo "sudo grep [...] ' $n ' | xargs sed -i 's/ $n / :term:`$n` /g' ./*.rst" >> $DESTINATIONPATH/$DESTINATION

然而,我假设您希望此文本稍后被解释为命令,无论您是否要执行您存储它们的文件,将它们复制/粘贴到shell或任何其他方式。如果是这种情况,并且如果EXP1和$n的其他值在那时不应该被解释为命令,那么您实际上需要双转义反引号:

echo "sudo grep [...] ' $n ' | xargs sed -i 's/ $n / :term:\`$n\` /g' ./*.rst" >> $DESTINATIONPATH/$DESTINATION

在这个命令中,我们有一个转义的反斜杠后面跟着一个转义的反斜杠,它将被当前脚本解释,并让它输出下面的命令,我们在其中找到了一个转义的反斜杠:

sudo grep [...] ' $n ' | xargs sed -i 's/ $n / :term:`$n` /g' ./*.rst

这样,当您执行此命令时,转义的反打号将被解释为实际字符,而不是被解析为命令替换,因为没有这样的命令而引发错误,并破坏您的sed命令。

正如你所看到的,这一切都相当复杂,这就是为什么我敦促你停止生成代码,而是将你的代码模板存储到脚本文件或函数中,然后简单地生成对它们的调用。

对于下面的脚本,我们将其命名为do_the_thing.sh:

#!/bin/bash
sudo grep [...] ' $1 ' | xargs sed -i 's/ $1 / :term:`$1` /g' ./*.rst

注意,我已经用$1代替了$n,这是对这个脚本将被调用的第一个参数的引用。

此时可以用/path/to/do_the_thing.sh "$n"替换当前脚本的循环体,而不用担心双重解释的影响,因为当您生成的调用被执行时,实际的代码只会被解释一次。

注意,你需要使脚本可执行(chmod +x /path/to/do_the_thing.sh),你可以使用一个相对路径(例如do_the_thing.sh "$n"),如果你生成的调用是从do_the_thing.sh脚本所在的同一目录执行的。

此外,由于这个额外脚本带来的简单性,您可能会质疑是否需要生成脚本。毕竟,执行当前尝试生成的5个命令将变得像

一样简单
for param in 'EXP1' 'EXP2' 'EXP3 EXP4' 'EXP5 EXP6'; do
/path/to/do_the_thing.sh "$param"
done