web-dev-qa-db-ja.com

HEREDOCテキストをシェルスクリプト変数に取り込むにはどうすればよいですか?

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

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

正しい出力:

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

何か案は?

9
Kevin

問題は、in Bash$( ... )エスケープ(およびその他の)シーケンス内で、ヒアドキュメント自体が解析されない場合でも解析されることです。それらを持っている。 \は改行をエスケープするため、2行になります。あなたが見ているのは本当にBashの解析の問題です-他のシェルはこれを行いません。古いバージョンでは、バックティックが問題になることもあります。私は これがBashのバグであることを確認済み を持っています。これは将来のバージョンで修正される予定です。

少なくとも関数を大幅に簡略化できます。

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

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

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

または、evalがないかなり醜いもの:

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

{}ではなく()が必要です。これにより、変数は後で利用できるようになります。

これを実行する頻度と目的に応じて、これらのオプションのいずれかを選択できます。最後の1つは、1回限りの場合は最も簡潔です。


zshを使用できる場合、元のコマンド置換+ heredocはそのまま動作しますが、これをすべて折りたたむこともできます。

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

Bashはこれをサポートしておらず、あなたが抱えている問題を経験する他のシェルもそうではないと思います。

11
Michael Homer

末尾の改行をサポートするために、@ MichaelHomerからの回答と私の元のソリューションを組み合わせました。最初の1つは魔法の文字列を使用し、最後の2つはPOSIXに準拠していなかったため、@ EliahKaganが指摘したリンクから提案された回避策を使用しませんでした。

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