题 清晰的方法将复杂的多行字符串写入变量


我需要将一些复杂的xml写入bash脚本中的变量。 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

94
2017-10-08 10:54




我愿意打赌你只是将这些数据再次转储到流中。为什么在可以使事情变得更复杂并使用流时将其存储在变量中? - Zenexer
看到这个: 带有额外空间的多行字符串(保留缩进) - Yibo Yang


答案:


这会将您的文本放入变量中而无需转义引号。它还将处理不平衡的报价(撇号,即 ')。在引号(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

123
2017-10-08 12:37



+1避免 cat。 - James Sneeringer
cat 是一个外部命令。不使用它可以节省这样做。另外,有些人的理念是,如果你使用少于两个论点的猫“Ur doin'就错了”(这与“无用的使用”截然不同 cat“)。 - Dennis Williamson
永远不会缩进第二个EOF ....(涉及多个桌子到头部的刘海) - IljaBek
我尝试使用上面的语句 set -e。它似乎 read 总是返回非零。您可以使用加厚此行为 ! read -d ....... - krissi
如果你使用这个多线 String 变量写入文件,将变量放在“QUOTES”周围 echo "${String}" > /tmp/multiline_file.txt 要么 echo "${String}" | tee /tmp/multiline_file.txt。花了一个多小时才找到它。 - Aditya


你几乎就在那里。要么你用  用于汇编字符串或引用整个字符串(在这种情况下,您必须转义字符串中的引号):

#!/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}"

25
2017-10-08 11:14



不幸的是,“Raphael's”中的撇号使第一个不起作用。 - Dennis Williamson
这两项任务最终都适合我。 VAR1中的单引号应该不是问题(至少不是bash)。也许你被语法高亮误导了? - joschi
它适用于脚本,但不适用于Bash提示符。很抱歉没有更清楚。 - Dennis Williamson
引用EOF更好 'EOF' 要么 "EOF"否则将解析shell变量。 - Stanislav German-Evtushenko


#!/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环境中正常工作


9
2017-09-05 15:54



+1此解决方案允许变量替换,如$ {foo} - Offirmo
上行:sh兼容。缺点:反击在bash中被弃用/劝阻。现在如果我不得不在sh和bash之间做出选择...... - Zenexer
从什么时候开始反对/不鼓励?只是好奇 - Alexander Mills


另一种做同样的方法......

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

#!/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 确实使用修改过的 IFS
  • 没有叉子。

4
2017-07-22 13:45



注意 mapfile 需要Bash 4或更高版本。和语法 "${Pattern[*]}" 将数组转换为字符串 在引号中 (如示例代码所示)。 - Dennis Williamson
是的,bash 4非常好 新 什么时候提出这个问题。 - F. Hauri


在许多其他答案中有太多的角落案例。

为了绝对确定空格,制表符,IFS等没有问题,更好的方法是使用“heredoc”构造,但使用heredoc的内容编码 uuencode 如下所述:

https://stackoverflow.com/questions/6896025/#11379627


2
2018-05-17 09:10