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'
仕組みは次のとおりです。
HEREDOC
後続のテキストの拡張を避けるために、単語は(小さい)引用符で示されます。- "here doc"の内容は標準入力に
<<
。 - このオプションは Document Here の内容全体を
-d ''
強制的に読み込みます。read
- この
-r
オプションは、バックスラッシュで囲まれた文字の解釈を防ぎます。 - コアコマンドはと似ています
read var
。 - 最後の詳細の1つは、デフォルトの
IFS=''
IFS:から先行または末尾の文字を削除して読み取りを防止することですspacetabnewline。
kshでは、このオプションのNULL値は-d ''
効果がありません。
回避策として、テキストに「キャリッジリターン」文字が含まれていない場合は、aが-d $'\r'
機能します($'\r'
もちろん、aが各行の末尾に追加される場合)。
追加された要件(注釈)は、POSIX準拠のソリューションを作成することです。
POSIX
POSIXオプションでのみ実行されるように、このアイデアを拡張してください。これは主にfor
がないことを意味します。これにより、すべての行が強制的に読み取られます。これにより、一度に1行ずつキャプチャする必要があります。次に、末尾の新しい行を作成するには追加する必要があります(読み取り時に削除されるため)。-d
read
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)"