
env
シェル変数に出力を取得して印刷しようとしています。
#!/bin/sh
ENV_BEFORE=$(env)
printf $ENV_BEFORE
その結果、env
出力の単一変数が印刷されます。
echo
代わりに使用すると、printf
改行なしですべての出力を印刷します。
私がここで何を見逃しているのでしょうか?
答え1
問題は、変数への参照がないことです$ENV
。説明したようにman bash
:
文字を二重引用符で囲むと、履歴拡張が有効になっている場合は、$、`、\、!を除く引用符内のすべての文字のリテラル値が保持されます。 $と `文字は二重引用符の中で特別な意味を持ちます。 バックスラッシュは、$、`、"、\、または文字のいずれかが後に続く場合にのみ特別な意味を維持します。。
したがって、シーケンスを\n
二重引用符で囲むと、その意味が維持されます。そのため、参照がない場合は\n
単に一般的なものですn
。
$ printf \n
n$
そして引用すると:
$ printf "\n"
$
Bashでは、引用符を持たない変数はSplit + glob演算子を呼び出します。これは、変数が空白(または$IFS
特殊変数が設定されているすべての項目)に分割され、各結果単語がglob(一致するファイル名と一致するように拡張)として使用されることを意味します。あなたの問題はその「分割」部分にあります。
これを説明するために、より単純な複数行変数を見てみましょう。
$ var=$(printf "foo\nbar\n")
これで、シェルのset -x
デバッグ機能を使用して、何が起こっているのかを正確に確認できます。
$ echo $var
+ echo foo bar
foo bar
$ echo "$var"
+ echo 'foo
bar'
foo
bar
上に示すように、echo $var
(引用されていない)は分割+globに従うので、2つの別々の文字列とを$var
生成します。改行文字は Split+Glob で食べます。変数が参照されると、分割+globの影響を受けずに改行が維持され、参照されるので正しく解釈され、印刷されます。foo
bar
次の質問はprintf
嫌いですecho
。提供した内容を印刷するだけでなく、書式文字列も必要です。たとえば、次のようにprintf "color:%s" "green"
印刷されます。color:green
%s
green
また、与えられた書式文字列に適合しないすべての入力を無視します。したがって、実行すると、printf foo bar
フォーマット文字列とフォーマットに使用される変数printf
として扱われます。 noまたは同等の項目を置き換えることができるので、無視され、別々に印刷されます。foo
bar
%s
bar
bar
foo
$ printf $var
+ printf foo bar
foo
を実行すると、これが発生しますprintf $ENV_BEFORE
。変数が参照されないので、分割globは効果的に改行を空白に置き換え、最初に見えるprintf
「単語」だけを印刷します。
これを正しく行うには、書式文字列を使用して常に変数を引用します。
printf '%s\n' "$ENV_BEFORE"
答え2
printf
(with echo
)はデフォルトで改行を印刷しないため、明示的に通知する必要があります。
printf "${ENV_BEFORE}\n"
またはより良い:
printf '%s\n' "$ENV_BEFORE"
後ろに@don_crisstiコメントで正しく指摘したように、質問をもう一度読んでみると、あなたの状況が上記と異なると思います。
あなたの場合、問題は変数への参照がないことです$ENV_BEFORE
。
printf
変数を引用しないと、合計がどのように機能するかは次のとおりです。echo
printf
:引用符または書式指定子がない場合、最初の単語のみが印刷されます。
%
引用符があり、書式指定子がない場合は、拡張子に書式指定子があるすべての項目を考慮してブロックできます。引用符がなくフォーマット指定子があると、トークン化とパス名拡張の影響を受けるため、すべての値が出力で空白になるため、
IFS
予期しない結果が発生します。引用符と書式指定子を使用すると、目的の結果を得ることができます。つまり、拡張時に単語の区切りとパス名の拡張は行われません。
echo
:引用符がないと、変数拡張はトークン化とパス名拡張の影響を受けるため、目的の出力を取得できません。
変数拡張を使用すると、
quote
トークン化とパス名拡張の影響を受けないため、目的の結果を得ることができます。