この効果を避ける

この効果を避ける

なぜシェル構造$(...)末尾の改行文字の削除

#!/bin/bash

S='a
b
'
printf '%i [%s]\n' "${#S}" "$S"

T=$(printf '%s' "$S")
printf '%i [%s]\n' "${#T}" "$T"

これを印刷してください:

4 [a
b
]
3 [a
b]

この変数Sには2つの改行文字が含まれています。最初はprintf正しく印刷されます。次に、2番目は、評価を使用して正確に同じ結果をprintf印刷して割り当てます。S最後の3番目のショーT$(...)printf最後の改行文字がありませんT!なぜ?

sh、ksh、Zshを使用して同じ結果を得ました。

答え1

これは文書化された動作ですman bash

Bash はサブシェル環境でコマンドを実行し、コマンド置換をコマンドの標準出力に置き換え、末尾の改行を削除して拡張を実行します。

もちろん、「なぜ?」という質問に対する答えではないと言うこともできます。

正式な説明はわかりません。私の考えでは、これは一般的に必要なものなので、これは意味があると思います。改行文字を削除するよりも追加する方が簡単です。

# This is how people are used to print text
echo foo

# In the other scenario this would end in two newlines at the end
echo foo "$(whatever)"

# could behandled with echo -n or printf but who would consider that an improvement?

もう一つの理由は、いくつかのコマンドは末尾に改行文字を追加しますが、他のコマンドはそうではないからです。この動作は、ユーザーがこの違いを気にする必要がある負担を軽減します。

この効果を避ける

実際のコマンド出力を取得するには、次のようにします。

T=$(whatever; echo x)
T_real="${T%x}"

答え2

なぜ?役に立たない、標準にそう出ているから:

...コマンド置換をコマンドの標準出力に置き換えて、置換の末尾から1つ以上の<newline>文字シーケンスを削除します。

実際、おそらく常にこのように動作していたからです(私が知っている限り、POSIXはほとんど既存の動作をエンコードすることです)。

より便利には、次のようにすることができます。

echo "$(date +"%F %T") $(hostname): something happened..."

変える

t=$(date +"%F %T")
t=${t%
}
h=$(hostname)
h=${h%
}
echo "$t $h: something happened..."

つまり、単一の値を印刷するほとんどのコマンドは、コマンドラインでユーザーフレンドリーにするために末尾に改行文字を印刷します。 (シェルは、カーソルがコマンドの次の行の先頭にないことを検出する必要はありません。zshは検出しますが、他のほとんどはそうではありません)。不要な場合があり、コンテンツのみが必要です。これは、印刷だけでなく、特定のコマンドに値を引数として渡す場合にも当てはまります。

一般的なソリューションはい追加の文字を追加して削除します。

foo=$(printf 'foo\n'; echo .)
foo=${foo%.}
echo "<$foo>"

printfまたは、内部コマンド置換のみがある場合は、printf -v可能であれば以下を使用します。

printf -v foo 'foo\n'

関連情報