web-dev-qa-db-ja.com

ヒアドキュメント値をBashの変数に割り当てる方法は?

私はこの複数行の文字列を持っています(引用符が含まれています):

abc'asdf"
$(dont-execute-this)
foo"bar"''

Bashのheredocを使用してどのように変数に割り当てますか?

改行を保持する必要があります。

文字列内の文字をエスケープしたくありません。

310
Neil

catの無駄な使用を避け、これにより不一致の引用符をより適切に処理できます。

$ read -r -d '' VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF

エコーするときに変数を引用しないと、改行は失われます。引用すると、それらが保存されます:

$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''

ソースコードを読みやすくするためにインデントを使用する場合は、小なり記号の後にダッシュを使用します。インデントは、タブのみ(スペースなし)を使用して行う必要があります。

$ read -r -d '' VAR <<-'EOF'
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''
    EOF
$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''

代わりに、結果の変数の内容にタブを保持する場合は、IFSからタブを削除する必要があります。 here doc(EOF)のターミナルマーカーはインデントしないでください。

$ IFS='' read -r -d '' VAR <<'EOF'
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''
EOF
$ echo "$VAR"
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''

を押すと、コマンドラインにタブを挿入できます Ctrl-VTab。エディターを使用している場合は、どちらが機能するかによって、またはタブをスペースに自動的に変換する機能をオフにする必要がある場合があります。

442

$()を使用してcatの出力を変数に割り当てます:

VAR=$(cat <<'END_HEREDOC'
abc'asdf"
$(dont-execute-this)
foo"bar"''
END_HEREDOC
)

# this will echo variable with new lines intact
echo "$VAR"
# this will echo variable without new lines (changed to space character)
echo $VAR

開始END_HEREDOCを単一引用符で区切ることを確認してください。

末尾のヒアドキュメント区切り文字END_HEREDOCは、行上で単独でなければならないことに注意してください(そのため、括弧は次の行にあります)。

答えをくれた@ephemientに感謝します。

185
Neil

これはDennisメソッドのバリエーションであり、スクリプトではよりエレガントに見えます。

関数定義:

define(){ IFS='\n' read -r -d '' ${1} || true; }

使用法:

define VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF

echo "$VAR"

楽しい

追伸read -dをサポートしないシェルの'read loop'バージョンを作成しました。 set -euおよびnpaired backticksで動作するはずですが、十分にテストされていません:

define(){ o=; while IFS="\n" read -r a; do o="$o$a"'
'; done; eval "$1=\$o"; }
73
ttt

質問テキストにコメントするのに十分な担当者がいないので、回答としてここにコメントを追加します。

改行を保持するソリューションはまだありません。

これは真実ではありません-あなたはおそらくエコーの振る舞いに惑わされているだけでしょう:

echo $VAR # strips newlines

echo "$VAR" # preserves newlines

27
patspam
VAR=<<END
abc
END

あなたはそれを気にしない何か、すなわち割り当てにstdinをリダイレクトしているため、動作しません

export A=`cat <<END
sdfsdf
sdfsdf
sdfsfds
END
` ; echo $A

動作しますが、これを使用するのを妨げる可能性のあるバックティックがあります。また、バッククォートの使用を実際に避ける必要があります。コマンド置換表記$(..)を使用することをお勧めします。

export A=$(cat <<END
sdfsdf
sdfsdf
sdfsfds
END
) ; echo $A
27
l0st3d

分岐 Neilの答え 、多くの場合、varはまったく必要ありません。変数とほぼ同じ方法で関数を使用でき、インラインまたはreadベースのソリューションよりもはるかに読みやすくなります。 。

$ complex_message() {
  cat <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF
}

$ echo "This is a $(complex_message)"
This is a abc'asdf"
$(dont-execute-this)
foo"bar"''
9
dimo414

配列は変数なので、その場合はmapfileが機能します

mapfile y <<z
abc'asdf"
$(dont-execute-this)
foo"bar"''
z

次に、このように印刷できます

printf %s "${y[@]}"
8
Steven Penny

ヒアドキュメント値を変数に割り当てる

VAR="$(cat <<'VAREOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
VAREOF
)"

コマンドの引数として使用

echo "$(cat <<'SQLEOF'
xxx''xxx'xxx'xx  123123    123123
abc'asdf"
$(dont-execute-this)
foo"bar"''
SQLEOF
)"
3
bronze man

NULLを含む文字列を読み取らなければならないことに気づいたので、ここにanythingをスローするソリューションを示します。実際にNULLを処理している場合は、16進レベルで処理する必要があります。

$ cat> read.dd.sh

read.dd() {
     buf= 
     while read; do
        buf+=$REPLY
     done < <( dd bs=1 2>/dev/null | xxd -p )

     printf -v REPLY '%b' $( sed 's/../ \\\x&/g' <<< $buf )
}

証明:

$ . read.dd.sh
$ read.dd < read.dd.sh
$ echo -n "$REPLY" > read.dd.sh.copy
$ diff read.dd.sh read.dd.sh.copy || echo "File are different"
$ 

HEREDOCの例(^ J、^ M、^ Iを使用):

$ read.dd <<'HEREDOC'
>       (TAB)
>       (SPACES)
(^J)^M(^M)
> DONE
>
> HEREDOC

$ declare -p REPLY
declare -- REPLY="  (TAB)
      (SPACES)
(^M)
DONE

"

$ declare -p REPLY | xxd
0000000: 6465 636c 6172 6520 2d2d 2052 4550 4c59  declare -- REPLY
0000010: 3d22 0928 5441 4229 0a20 2020 2020 2028  =".(TAB).      (
0000020: 5350 4143 4553 290a 285e 4a29 0d28 5e4d  SPACES).(^J).(^M
0000030: 290a 444f 4e45 0a0a 220a                 ).DONE
1
Orwellophile

dimo414's answer のおかげで、これは彼の優れたソリューションがどのように機能するかを示しており、テキストに引用符と変数を簡単に含めることができることも示しています。

出力例

$ ./test.sh

The text from the example function is:
  Welcome dev: Would you "like" to know how many 'files' there are in /tmp?

  There are "      38" files in /tmp, according to the "wc" command

test.sh

#!/bin/bash

function text1()
{
  COUNT=$(\ls /tmp | wc -l)
cat <<EOF

  $1 Would you "like" to know how many 'files' there are in /tmp?

  There are "$COUNT" files in /tmp, according to the "wc" command

EOF
}

function main()
{
  OUT=$(text1 "Welcome dev:")
  echo "The text from the example function is: $OUT"
}

main
0
Brad Parks