HEREDOCテキストをシェルスクリプト変数にどのように入れますか?

HEREDOCテキストをシェルスクリプト変数にどのように入れますか?

POSIX互換の方法でHEREDOCテキストをシェルスクリプト変数に入れようとしています。私はそう試みる:

#!/bin/sh

NEWLINE="
"

read_heredoc2() {
  while IFS="$NEWLINE" read -r read_heredoc_line; do
    echo "${read_heredoc_line}"
  done
}

read_heredoc2_result="$(read_heredoc2 <<'HEREDOC'

                        _                            _ _
                       | |                          | (_)
  _ __ ___  _   _ _ __ | | __ _  ___ ___  ___  _ __ | |_ _ __   ___
 | '_ ` _ \| | | | '_ \| |/ _` |/ __/ _ \/ _ \| '_ \| | | '_ \ / _ \
 | | | | | | |_| | |_) | | (_| | (_|  __/ (_) | | | | | | | | |  __/
 |_| |_| |_|\__, | .__/|_|\__,_|\___\___|\___/|_| |_|_|_|_| |_|\___|
             __/ | |
            |___/|_|



HEREDOC
)"

echo "${read_heredoc2_result}"

これにより、次のエラーが発生しました。

                        _                            _ _
                       | |                          | (_)
  _ __ ___  _   _ _ __ | | __ _  ___ ___  ___  _ __ | |_ _ __   ___
 | '_ ` _ \| | | | '_ \| |/ _` |/ __/ _ \/ _ \| '_ \| | | '_ \ / _  | | | | | | |_| | |_) | | (_| | (_|  __/ (_) | | | | | | | | |  __/
 |_| |_| |_|\__, | .__/|_|\__,_|\___\___|\___/|_| |_|_|_|_| |_|\___|
             __/ | |
            |___/|_|

次の方法はうまくいきますが、ランダムな出力変数を使用するのがどれほど扱いにくいのか気にしません。

#!/bin/sh

NEWLINE="
"

read_heredoc1() {
  read_heredoc_first=1
  read_heredoc_result=""
  while IFS="$NEWLINE" read -r read_heredoc_line; do
    if [ ${read_heredoc_first} -eq 1 ]; then
      read_heredoc_result="${read_heredoc_line}"
      read_heredoc_first=0
    else
      read_heredoc_result="${read_heredoc_result}${NEWLINE}${read_heredoc_line}"
    fi
  done
}

read_heredoc1 <<'HEREDOC'

                        _                            _ _            
                       | |                          | (_)           
  _ __ ___  _   _ _ __ | | __ _  ___ ___  ___  _ __ | |_ _ __   ___ 
 | '_ ` _ \| | | | '_ \| |/ _` |/ __/ _ \/ _ \| '_ \| | | '_ \ / _ \
 | | | | | | |_| | |_) | | (_| | (_|  __/ (_) | | | | | | | | |  __/
 |_| |_| |_|\__, | .__/|_|\__,_|\___\___|\___/|_| |_|_|_|_| |_|\___|
             __/ | |                                                
            |___/|_|                                                



HEREDOC

echo "${read_heredoc_result}"

正しい出力:

                        _                            _ _            
                       | |                          | (_)           
  _ __ ___  _   _ _ __ | | __ _  ___ ___  ___  _ __ | |_ _ __   ___ 
 | '_ ` _ \| | | | '_ \| |/ _` |/ __/ _ \/ _ \| '_ \| | | '_ \ / _ \
 | | | | | | |_| | |_) | | (_| | (_|  __/ (_) | | | | | | | | |  __/
 |_| |_| |_|\__, | .__/|_|\__,_|\___\___|\___/|_| |_|_|_|_| |_|\___|
             __/ | |                                                
            |___/|_|                                                

どんなアイデアがありますか?

答え1

問題は、大きな打撃を受けた状態で、内部$( ... )エスケープ(およびその他)シーケンスは、区切り文字自体にそのシーケンスがない場合でも構文解析されます。改行文字がエスケープされているため、二重線\が表示されます。あなたが見ているのは実際にBashの解析問題です。他のシェルではこれを行いません。バックティックは以前のバージョンでも問題になる可能性があります。私は持っていますこれがBashのバグであることを確認しました。、今後のバージョンで修正される予定です。

少なくとも機能を大幅に簡素化できます。

func() {
    res=$(cat)
}
func <<'HEREDOC'
...
HEREDOC

出力変数を選択するにはパラメータ化できます。

func() {
    eval "$1"'=$(cat)'
}
func res<<'HEREDOC'
...
HEREDOC

または、次のことがないやや醜いものeval

{ res=$(cat) ; } <<'HEREDOC'
...
HEREDOC

必要なのは、後で変数を引き続き使用できるようにすることで{}はありません。()

これをどれだけ頻繁に実行し、どの目的で実行するかに応じて、1つまたは他のオプションを好むことができます。最後は最も簡潔なワンタイムです。


が利用可能な場合、zsh元のコマンドの置き換え+ heredocはそのまま機能しますが、これらの項目をさらに縮小することもできます。

x=$(<<'EOT'
...
EOT
)

Bashはこれをサポートしておらず、他のシェルにはあなたが持っている問題がないと思います。

答え2

OPソリューション情報:

  • 特定の定数変数が許可されている場合、変数を割り当てるためにevalは必要ありません。

  • HEREDOCを受け取る関数を呼び出す一般的な構造を実装することもできます。

すべての(合理的な)シェルで動作する2つの問題の解決策は次のとおりです。

#!/bin/bash
nl="
"

read_heredoc(){
    var=""
    while IFS="$nl" read -r line; do
        var="$var$line$nl"
    done 
}


read_heredoc <<'HEREDOC'

                        _                            _ _
                       | |                          | (_)
  _ __ ___  _   _ _ __ | | __ _  ___ ___  ___  _ __ | |_ _ __   ___
 | '_ ` _ \| | | | '_ \| |/ _` |/ __/ _ \/ _ \| '_ \| | | '_ \ / _ \
 | | | | | | |_| | |_) | | (_| | (_|  __/ (_) | | | | | | | | |  __/
 |_| |_| |_|\__, | .__/|_|\__,_|\___\___|\___/|_| |_|_|_|_| |_|\___|
             __/ | |
            |___/|_|

HEREDOC

read_heredoc2_result="$str"

printf '%s' "${read_heredoc2_result}"

元の問題に対する解決策。

bash 2.04(最近はzsh、lksh、mksh)から動作したソリューションです。
以下の移植性の高い(POSIX)バージョンをご覧ください。

#!/bin/bash
read_heredoc() {
    IFS='' read -d '' -r var <<'HEREDOC'

                        _                            _ _
                       | |                          | (_)
  _ __ ___  _   _ _ __ | | __ _  ___ ___  ___  _ __ | |_ _ __   ___
 | '_ ` _ \| | | | '_ \| |/ _` |/ __/ _ \/ _ \| '_ \| | | '_ \ / _ \
 | | | | | | |_| | |_) | | (_| | (_|  __/ (_) | | | | | | | | |  __/
 |_| |_| |_|\__, | .__/|_|\__,_|\___\___|\___/|_| |_|_|_|_| |_|\___|
             __/ | |
            |___/|_|



HEREDOC

}

read_heredoc
echo "$var"

コアコマンド

IFS='' read -d '' -r var <<'HEREDOC'

仕組みは次のとおりです。

  1. HEREDOC後続のテキストの拡張を避けるために、単語は(小さい)引用符で示されます。
  2. "here doc"の内容は標準入力に<<
  3. このオプションは Document Here の内容全体を-d ''強制的に読み込みます。read
  4. この-rオプションは、バックスラッシュで囲まれた文字の解釈を防ぎます。
  5. コアコマンドはと似ていますread var
  6. 最後の詳細の1つは、デフォルトのIFS=''IFS:から先行または末尾の文字を削除して読み取りを防止することですspacetabnewline

kshでは、このオプションのNULL値は-d ''効果がありません。
回避策として、テキストに「キャリッジリターン」文字が含まれていない場合は、aが-d $'\r'機能します($'\r'もちろん、aが各行の末尾に追加される場合)。


追加された要件(注釈)は、POSIX準拠のソリューションを作成することです。

POSIX

POSIXオプションでのみ実行されるように、このアイデアを拡張してください。これは主にfor
がないことを意味します。これにより、すべての行が強制的に読み取られます。これにより、一度に1行ずつキャプチャする必要があります。次に、末尾の新しい行を作成するには追加する必要があります(読み取り時に削除されるため)。-dread

var

#!/bin/sh

nl='
'

read_heredoc() {
    unset var
    while IFS="$nl" read -r line; do
        var="$var$line$nl"
    done <<\HEREDOC

                        _                            _ _
                       | |                          | (_)
  _ __ ___  _   _ _ __ | | __ _  ___ ___  ___  _ __ | |_ _ __   ___
 | '_ ` _ \| | | | '_ \| |/ _` |/ __/ _ \/ _ \| '_ \| | | '_ \ / _ \ 
 | | | | | | |_| | |_) | | (_| | (_|  __/ (_) | | | | | | | | |  __/ 
 |_| |_| |_|\__, | .__/|_|\__,_|\___\___|\___/|_| |_|_|_|_| |_|\___| 
             __/ | | 
            |___/|_| 



HEREDOC

}

read_heredoc
printf '%s' "$var"

これはすべての合理的なシェルで動作し、テストされました。

答え3

末尾の改行をサポートするために、@MichaelHomerの答えを元の解決策と組み合わせました。 @EliahKaganが指摘したリンクで提案された回避策を使用していません。最初は魔法の文字列を使用し、最後の2つはPOSIXと互換性がないためです。

#!/bin/sh

NEWLINE="
"

read_heredoc() {
  read_heredoc_result=""
  while IFS="${NEWLINE}" read -r read_heredoc_line; do
    read_heredoc_result="${read_heredoc_result}${read_heredoc_line}${NEWLINE}"
  done
  eval $1'=${read_heredoc_result}'
}

read_heredoc heredoc_str <<'HEREDOC'

                        _                            _ _
                       | |                          | (_)
  _ __ ___  _   _ _ __ | | __ _  ___ ___  ___  _ __ | |_ _ __   ___
 | '_ ` _ \| | | | '_ \| |/ _` |/ __/ _ \/ _ \| '_ \| | | '_ \ / _ \
 | | | | | | |_| | |_) | | (_| | (_|  __/ (_) | | | | | | | | |  __/
 |_| |_| |_|\__, | .__/|_|\__,_|\___\___|\___/|_| |_|_|_|_| |_|\___|
             __/ | |
            |___/|_|




HEREDOC

echo "${heredoc_str}"

答え4

catの無駄な使用(引用符\と`):

myplaceonline="
                       _                            _ _            
 _ __ ___  _   _ _ __ | | __ _  ___ ___  ___  _ __ | (_)_ __   ___ 
| '_ \` _ \\| | | | '_ \\| |/ _\` |/ __/ _ \\/ _ \\| '_ \\| | | '_ \\ / _ \\
| | | | | | |_| | |_) | | (_| | (_|  __/ (_) | | | | | | | | |  __/
|_| |_| |_|\\__, | .__/|_|\\__,_|\\___\\___|\\___/|_| |_|_|_|_| |_|\\___|
       |___/|_
"

または引用符なしで:

myplaceonline="$(figlet myplaceonline)"

関連情報