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


374

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

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

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

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

文字列の文字をエスケープしたくないので、迷惑です...


@JohnM-ヒアドキュメントの割り当てを一重引用符'EOF'で試しましたが、` in the content: if the second line has cd`コマンドで改行をエスケープしました。「. sh :行X:cd:コマンドが見つかりません」; しかし、もし私が二重引用符を付けると"EOF"、その場合、bash変数${A}は文字列として保持されません(展開されます)。しかし、改行保持されます-そして、私はcd2行目でコマンドを実行する問題はありません(そして、「EOF」と「EOF」の両方はeval、に保存されている一連のコマンドを実行するために、文字列変数)。乾杯!
sdaau

1
...そして、前のコメントに追加します。二重引用符で囲まれた"EOF"変数のbashコメント "#"を介して呼び出された場合eval $VAR、ここでは$ VARが1行として表示されるため、スクリプトの残りのすべてにコメントが付けられます。 ; #複数行スクリプトでbash コメントを使用できるようにするには、eval call: eval "$ VAR" ` でも変数を二重引用符で囲みます。
sdaau

@sdaau:evalこのメソッドで問題が発生しevalましたが、設定ファイルで定義されているいくつかの変数を含むパッケージの一部であるため、追跡できませんでした。エラーメッセージ:/usr/lib/network/network: eval: line 153: syntax error: unexpected end of file。別のソリューションに切り替えました。
Golar Ramblar

回答:


515

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- を押すと、コマンドラインにタブを挿入できますV Tab。エディターを使用している場合は、エディターに応じて機能するか、タブをスペースに自動的に変換する機能をオフにする必要があります。


117
スクリプトにset -o errexit(別名set -e)があり、これを使用すると、readEOFに達したときにゼロ以外の戻りコードを返すため、スクリプトが終了することを言及する価値があると思います。
Mark Byers、2011年

14
@MarkByers:これは私が使用set -eしたことがない理由の1つであり、常に使用しないことをお勧めします。代わりに適切なエラー処理を使用することをお勧めします。trapあなたの友だちです。他の友人:elseおよび||他の中。
追って通知があるまで一時停止。

7
catそのような場合、本当に価値のある回避策はありますか?ヒアドキュメントを変数に割り当てることcatはよく知られているイディオムです。どういうわけか、read難読化されたものを使用することは、ほとんど何の利益にもなりません。
Gregory Pakosz 14年

6
@ulidtkoこれは、dと空の文字列の間にスペースがないためです。引数bashが表示-rd''される-rd前に単純に折りたたまれるreadのでVAR、への引数として扱われます-d
chepner 2014

6
この形式でreadは、ゼロ以外の終了コードで戻ります。このため、エラーチェックが有効になっているスクリプト(などset -e)では、この方法は理想的ではありません。
スイス

245

$()を使用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答えてくれてありがとう。


1
実際には、これはいくつかの状況で引用符を通過させます。私はなんとかPerlでこれを簡単に行うことができました...
Neil

32
+1。これは、少なくとも私の目には、最も読みやすいソリューションです。変数の名前を読み取りコマンドに埋め込むのではなく、ページの左端に残します。
クレイトンスタンレー

12
PSA:改行を保持するには、変数を引用符で囲む必要があることに注意してください。echo "$VAR"の代わりにecho $VAR
sevko 2014年

7
これはashreadがサポートしていないOpenWRTで便利です-d
David Ehrmann、2015年

3
理解できない理由で、ヒアドキュメントにペアになっていないバックティックがある場合、これは「予期しないEOF」エラーで失敗します。
Radon Rosborough、2016年

79

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

関数定義:

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

使用法:

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

echo "$VAR"

楽しい

psは、をサポートしないシェル用の「読み取りループ」バージョンを作成しましたread -dset -euペアになっていないバックティックで動作するはずですが、十分にテストされていません:

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

1
これは表面的にのみ機能するようです。定義関数は1のステータスを返しますが、何を修正する必要があるのか​​よくわかりません。
2012年

1
これは、bash(read関数内のループ、-d ''改行を保持するために必要なbashism を回避するためのループ)に加えてPOSIX shをサポートするように変更できるため、受け入れられた回答よりも優れています。
ELLIOTTCABLE 2014

cat-in-a-subshel​​lオプションとは異なり、これは、ヒアドキュメントのペアになっていないバックティックで機能します。ありがとうございました!
Radon Rosborough、2016年

このソリューションはset -eセットで機能しますが、選択した回答は機能しません。それは理由のようですhttp://unix.stackexchange.com/a/265151/20650
初出の

2
@fny psの返却ステータスが長い間修正されました
ttt

36
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

$(実行可能)を含めるように質問を更新しました。また、どのように改行を保存しますか?
ニール、

2
@ l0st3d:とても近い... $(cat <<'END'代わりに使用してください。@ニール:最後の改行は変数の一部にはなりませんが、残りは保持されます。
ephemient

1
改行が保持されているようには見えません。上記の例を実行すると、「sdfsdf sdfsdf sdfsfds」...ああ!しかし、書き込みecho "$A"(つまり、$ Aを二重引用符で囲む)すると、改行が表示されます。
ダレン・クック

1
@ダレン:ああ!私は改行の問題に気づいており、出力変数を引用符で囲むと問題が解決します。THX!
javadba 2013年

1
興味深いことに、最初の例の癖のため、ピンチでこれを次のようにその場しのぎのコメントブロックに使用できますREM=<< 'REM' ... comment block goes here ... REM。またはもっとコンパクトに: << 'REM' ...。「REM」は「NOTES」や「SCRATCHPAD」などのようなものである可能性があります
Beejor

34

改行を保持する解決策はまだありません。

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

echo $VAR # strips newlines

echo "$VAR" # preserves newlines


5
本当にこれは、変数のクォートがどのように機能するかの振る舞いです。引用符がない場合は、スペースで区切られた異なるパラメーターとして挿入されますが、引用符がある場合は、変数の内容全体が1つの引数として扱われます
Czipperz

11

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

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

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

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

printf %s "${y[@]}"

3

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

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
)"

最初の方法を試したところ、行と行の間に行末記号がないようです。私のLinuxマシンで何らかの設定が必要ですか?
Kemin Zhou

これはおそらく、手段、あなたの変数をエコーされたとき、あなたはそれの前後に引用符を入れていなかった...ので、好きなことを試してみてくださいecho "$VAR"
ブラッド公園

1

私はNULLが含まれている文字列を読み取る必要があることに気づいたので、これは何でも読み取るソリューションですスローしたをます。ただし、実際に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

0

dimo414の回答に感謝、これは彼の優れたソリューションがどのように機能するかを示し、テキスト内に引用符と変数を簡単ことができることを示しています。

出力例

$ ./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

それがどのように処理されるかを確認するために、テキスト内で一致しない引用を見るのは興味深いでしょう。たぶん `気を悪くしないでください、「$ COUNT」ファイルがあるので、アポストロフィ/単一引用符は物事を面白くすることができます。
dragon788

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