複雑な複数行の文字列を変数に書き込むクリーンな方法


109

複雑な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

私はあなたがそのデータを再びストリームにダンプするだけであると確信しています。物事をより複雑にし、ストリームを使用できるのに、なぜ変数に格納するのですか?
ゼネクサー

回答:


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
ジェームズ・スニーリンガー

4
cat外部コマンドです。それを使用しないことはそれをすることを節約します。加えて、2つ未満の引数でcatを使用している場合、「Ur doin 'it wrong」(「無駄な使用」とは異なります)という哲学がありますcat
デニスウィリアムソン

9
2番目のEOFをインデントすることはありません。...(複数のテーブルから
ヘッドバンを

9
私は上記のステートメントを使用しようとしましたset -e。それはそうですread、常に非ゼロを返します。使用してこの動作を厚くすることができます! read -d .......
krissi

11
また、この複数行のString変数を使用してファイルに書き込む場合は、echo "${String}" > /tmp/multiline_file.txtまたはのような「QUOTES」の周りに変数を配置しますecho "${String}" | tee /tmp/multiline_file.txt。それを見つけるのに1時間以上かかった。
アディティア14

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には問題ありません)。たぶん、構文の強調表示に惑わされているのでしょうか?
joschi

1
スクリプトで機能しますが、Bashプロンプトでは機能しません。はっきりしないのでごめんなさい。
デニスウィリアムソン

1
'EOF'またはとしてEOFを引用"EOF"することをお勧めします。そうしないと、シェル変数が解析されます。
スタニスラフジャーマンエフシェンコ

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シェル環境内で正常に機能するはずです。


1
+1このソリューションは、$ {foo}のような変数置換を許可します
Offirmo

利点:sh互換。欠点:バックスティックはbashで非推奨/非推奨になりました。今、shとbashのどちらかを選択する必要があった場合
...-Zenexer

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は、変更されたのみを使用してくださいIFS
  • フォークなし。

mapfileBash 4以降が必要です。また、構文は引用符で囲まれている場合に"${Pattern[*]}"配列を文字列キャストします(コード例に示すように)。
デニスウィリアムソン

はい、bash 4はこの質問が出されたときは非常に新しいものでした。
F.ハウリ

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.