将复杂的多行字符串写入变量的干净方法


109

我需要在bash脚本中的变量中写入一些复杂的xml。xml需要在bash脚本中可读,因为这是xml片段所在的位置,而不是从另一个文件或源中读取的。

所以我的问题是,如果我想在bash脚本中使用一个长字符串以使其易于阅读,那么最好的处理方法是什么?

理想情况下,我想要:

  • 不必逃脱任何角色
  • 跨多行使其可读
  • 保持缩进

可以用EOF或其他方式完成此功能,有人可以给我一个例子吗?

例如

String = <<EOF
 <?xml version="1.0" encoding='UTF-8'?>
 <painting>
   <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
   <caption>This is Raphael's "Foligno" Madonna, painted in
   <date>1511</date>-<date>1512</date>.</caption>
 </painting>
EOF

我敢打赌,您将再次将数据转储到流中。当您可以使事情变得更复杂并使用流时,为什么将其存储在变量中?
Zenexer 2013年

Answers:


140

这会将您的文本放入变量中,而无需转义引号。它还将处理不平衡的报价(撇号,即')。在前哨(EOF)周围加上引号可防止文本进行参数扩展。的-d''原因它来读取多行(忽略新行)。read是Bash内置的,因此不需要调用外部命令cat

IFS='' read -r -d '' String <<"EOF"
<?xml version="1.0" encoding='UTF-8'?>
 <painting>
   <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
   <caption>This is Raphael's "Foligno" Madonna, painted in
   <date>1511</date>-<date>1512</date>.</caption>
 </painting>
EOF

17
为避免+1 cat
James Sneeringer,2009年

4
cat是一个外部命令。不使用它可以节省时间。另外,有些人的哲学是,如果您使用的猫带有少于两个参数,“ Ur doin'it错”(这与“无用的使用cat”不同)。
丹尼斯·威廉姆森

9
并且永远不会缩进第二个EOF ....(涉及多个表以
敲击

9
我尝试在时使用上述陈述set -e。似乎read总是返回非零。您可以使用! read -d .......
krissi 2012年

11
并且,如果您使用此多行String变量来写入文件,请将该变量放在“ QUOTES”周围,例如echo "${String}" > /tmp/multiline_file.txtecho "${String}" | tee /tmp/multiline_file.txt。花了我一个多小时才找到。
2014年

28

你已经快到了。您可以使用cat来组装字符串,或者对整个字符串加引号(在这种情况下,您必须将字符串内的引号转义):

#!/bin/sh
VAR1=$(cat <<EOF
<?xml version="1.0" encoding='UTF-8'?>
<painting>
  <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
  <caption>This is Raphael's "Foligno" Madonna, painted in
  <date>1511</date>-<date>1512</date>.</caption>
</painting>
EOF
)

VAR2="<?xml version=\"1.0\" encoding='UTF-8'?>
<painting>
  <img src=\"madonna.jpg\" alt='Foligno Madonna, by Raphael'/>
  <caption>This is Raphael's \"Foligno\" Madonna, painted in
  <date>1511</date>-<date>1512</date>.</caption>
</painting>"

echo "${VAR1}"
echo "${VAR2}"

不幸的是,“ Raphael's”中的撇号使第一个无效。
丹尼斯·威廉姆森

两项任务最终都对我有用。VAR1中的单引号应该没有问题(至少对于bash而言不是)。也许您被语法高亮误导了?
09年

1
它可以在脚本中工作,但不能在Bash提示符下工作。抱歉,不清楚。
丹尼斯·威廉姆森

1
最好将EOF引用为'EOF'"EOF",否则将解析shell变量。
斯坦尼斯拉夫·德·伊夫图申科

13
#!/bin/sh

VAR1=`cat <<EOF
<?xml version="1.0" encoding='UTF-8'?>
<painting>
  <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
  <caption>This is Raphael's "Foligno" Madonna, painted in
  <date>1511</date>-<date>1512</date>.</caption>
</painting>
EOF
`
echo "VAR1: ${VAR1}"

在Bourne shell环境中应该可以正常工作


1
+1该解决方案允许变量替换,例如$ {foo}
Offirmo 2012年

上行:sh兼容。不足之处:Bash中不赞成使用反引号。现在,如果我不得不在sh和bash之间进行选择...
Zenexer 2013年

2
因为反引号何时过时/变粘?只是好奇
亚历山大·米尔斯

6

还有另一种方法可以做到这一点...

我喜欢使用变量和特殊的<<-谁在每一行的开头删除制表符以允许脚本缩进:

#!/bin/bash

mapfile Pattern <<-eof
        <?xml version="1.0" encoding='UTF-8'?>
        <painting>
          <img src="%s" alt='%s'/>
          <caption>%s, painted in
          <date>%s</date>-<date>%s</date>.</caption>
        </painting>
        eof

while IFS=";" read file alt caption start end ;do
    printf "${Pattern[*]}" "$file" "$alt" "$caption" "$start" "$end"
  done <<-eof
        madonna.jpg;Foligno Madonna, by Raphael;This is Raphael's "Foligno" Madonna;1511;1512
        eof

警告:之前没有空格eof只有制表符

<?xml version="1.0" encoding='UTF-8'?>
 <painting>
   <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
   <caption>This is Raphael's "Foligno" Madonna, painted in
   <date>1511</date>-<date>1512</date>.</caption>
 </painting>
一些解释:
  • mapfile将整个文档读取到一个数组中。
  • 语法"${Pattern[*]}"确实将此数组转换为字符串。
  • 我使用IFS=";"是因为;必需的字符串中没有
  • 该语法while IFS=";" read file ...禁止IFS在脚本的其余部分进行修改。在这种情况下,只能read使用Modifyed IFS
  • 没有叉子

请注意,mapfile需要Bash 4或更高版本。而且语法用引号"${Pattern[*]}"将数组转换为字符串(如示例代码所示)。
丹尼斯·威廉姆森

是的,当问这个问题时,bash 4还是很新的
F. Hauri

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.