web-dev-qa-db-ja.com

1行だけを印刷するPrintf

シェル変数でenvの出力を取得して出力しようとしています。

#!/bin/sh
ENV_BEFORE=$(env)
printf $ENV_BEFORE

その結果、env出力からの単一の変数が出力されます。

echoの代わりにprintfを使用すると、すべての出力が印刷されますが、改行は含まれません。

ここで何が欠けていますか?

問題は、$ENV変数を引用していないことです。 man bashで説明されているように:

文字を二重引用符で囲むと、$、 `、\、および履歴拡張が有効な場合は!を除いて、引用符内のすべての文字のリテラル値が保持されます。文字$および `は、二重引用符で囲まれた特別な意味を保持します。 円記号は、$、 `、"、\、またはのいずれかの文字が後に続く場合にのみ、特別な意味を保持します。

したがって、\nのようなシーケンスを二重引用符で囲むと、その意味が保持されます。これが、引用されていない場合、\nが通常のnである理由です。

$ printf \n
n$

一方、引用すると:

$ printf "\n"

$

Bash内の引用符で囲まれていない変数は、split + glob演算子を呼び出します。これは、変数が空白(または特別な変数$IFSに設定されているもの)で分割され、結果の各Wordがグロブとして使用されることを意味します(一致するファイル名に一致するように展開されます)。あなたの問題は、この「分割」部分にあります。

説明のために、より簡単な複数行変数を見てみましょう。

$ var=$(printf "foo\nbar\n")

これで、シェルのset -xデバッグ機能を使用して、何が起こっているのかを正確に確認できます。

$ echo $var
+ echo foo bar
foo bar

$ echo "$var"
+ echo 'foo
bar'
foo
bar

上記を見るとわかるように、echo $var(引用符で囲まれていない)は$varをsplit + globにサブジェクトするため、2つの別々の文字列foobarになります。改行は、split + globによって食べられました。変数が引用符で囲まれたとき、それはsplit + globの対象ではなく、改行が保持され、引用符で囲まれているため、正しく解釈され、印刷されます。

次の問題は、printfechoと似ていないことです。それはあなたが与えるものを単に印刷するだけでなく、フォーマット文字列を期待します。たとえば、printf "color:%s" "green"greenに置き換えられるため、color:green%sを出力します。

また、指定されたフォーマット文字列に収まらない入力は無視されます。したがって、printf foo barを実行すると、printffooをフォーマット文字列として扱い、barはフォーマットする変数として扱います。 barに置き換えられる%sまたは同等のものがないため、barは無視され、fooのみが出力されます。

$ printf $var
+ printf foo bar
foo

printf $ENV_BEFOREを実行すると、それが起こりました。変数が引用されていないため、分割グロブは改行をスペースで効果的に置き換え、printfは最初に見つかった「Word」のみを出力しました。

正しく行うには、フォーマット文字列を使用し、常に変数を引用符で囲みます。

printf '%s\n' "$ENV_BEFORE"
9
terdon

printfechoとは異なり)はデフォルトでは改行を出力しないため、明示的に指定する必要があります。

printf "${ENV_BEFORE}\n"

以上:

printf '%s\n' "$ENV_BEFORE"

@ don_crissti がコメントで正しく指摘された後、もう一度質問を読んだ後、あなたのケースは上記とは異なると思います。

あなたの場合、問題は変数$ENV_BEFOREを引用していないことです。

変数を引用符で囲まない場合のprintfechoの動作は次のとおりです。

  • printf

    1. 引用符と書式指定子がない場合、最初のWordのみが印刷されます

    2. 引用符を使用し、フォーマット指定子を使用しない場合、%をフォーマット指定子として持つ展開のすべてが考慮され、窒息する可能性があります

    3. 引用符なしでフォーマット指定子を使用すると、Wordの分割とパス名の展開が行われるため、IFSの値が出力のスペースになるため、予期しない結果が発生します。

    4. 引用符とフォーマット指定子を使用すると、目的の結果が得られます。つまり、展開でWordの分割とパス名の展開は実行されません。

  • echo

    1. 引用符がないと、変数の展開はWordの分割とパス名の展開の対象となるため、目的の出力が得られません。

    2. quoteを使用すると、変数の展開はWordの分割やパス名の展開の影響を受けないため、目的の出力が得られます

4
heemayl