末尾の改行を削除せずにstdinを変数にキャプチャするにはどうすればよいですか?


9

シェルスクリプトで...

末尾の改行を削除せずにstdinを変数にキャプチャするにはどうすればよいですか?

今私は試しました:

var=`cat`
var=`tee`
var=$(tee)

すべての場合$varにおいて、入力ストリームの末尾の改行はありません。ありがとう。

また、入力に後続の改行がない場合、ソリューションは追加しません。

承認された回答に照らして更新:

私のコードで使用した最終的な解決策は次のとおりです。

function filter() {
    #do lots of sed operations
    #see https://github.com/gistya/expandr for full code
}

GIT_INPUT=`cat; echo x`
FILTERED_OUTPUT=$(printf '%s' "$GIT_INPUT" | filter)
FILTERED_OUTPUT=${FILTERED_OUTPUT%x}
printf '%s' "$FILTERED_OUTPUT"

完全なコードを確認したい場合は、エキスパンダーのgithubページを参照してください。エキスパンダーは、情報セキュリティの目的で開発した小さなオープンソースのgitキーワード展開フィルターシェルスクリプトです。.gitattributesファイル(ブランチ固有にすることができます)およびgit configで設定されたルールに従って、git は、リポジトリにチェックインまたはチェックアウトするたびに、expandr.shシェルスクリプトを介して各ファイルをパイプ処理します。(そのため、末尾の改行またはその欠如を保持することが重要でした。)これにより、機密情報をクレンジングし、テスト、ステージング、およびライブブランチの環境固有の値の異なるセットを交換できます。


ここで行うことは必要ありません。filter取るstdin-それが実行されsedます。あなたは、キャッチstdin$GIT_INPUTまでその背中を印刷し、その後stdoutオーバーパイプにfilter、そのキャッチstdoutでを$FILTERED_OUTPUTして、背中にそれを印刷しますstdout。上記の例の下部にある4行はすべて、次のように置き換えることができますfilter。ここでの違反は意味しません、それはただ...あなたは働きすぎです。ほとんどの場合、シェル変数は必要ありません。入力を適切な場所に送信して渡すだけです。
mikeserv 2014

いいえ、ここで何をする必要があります。なぜなら、単にを実行するとfilter、最初に改行で終わっていなかった入力ストリームの最後に改行文字が追加されるからです。実際、私は元々そうしていましたfilterが、「常に改行を追加する」も「常に改行を削除する」も許容できるソリューションではないため、この解決策に導いたその問題に遭遇しました。
CommaToast 2014

sedおそらく余分な改行を行います-しかしfilter、残りのすべてではなくそれを処理する必要があります。そして、あなたが持っているそれらのすべての機能は基本的に同じことをします-a sed s///。シェルを使用して、メモリに保存したデータをパイプしているsedため、そのsedデータを、シェルがメモリに格納している他のデータに置き換えて、sedパイプでシェルに戻すことができます。なぜ[ "$var" = "$condition" ] && var=new_valueですか?私はまた、配列を得ることはありません-あなたは、アレイの名前を格納している[0]使用して、その後sedの値とそれを置き換えるために[1]?たぶんチャット?
mikeserv 2014

@mikeserv-そのコードを内部に移動する利点は何でしょうfilterか?それは現状のまま完全に機能します。私のリンクのコードがどのように機能するか、そしてなぜ私が自分のやり方でそれをセットアップしたのかについて、ええ、チャットルームでそれについて話しましょう。
CommaToast 2014

回答:


7

値が変数に格納される前に、末尾の改行が削除されます。あなたは次のようなことをしたいかもしれません:

var=`cat; echo x`

${var%x}代わりに使用します$var。例えば:

printf "%s" "${var%x}"

これは後続の改行の問題を解決しますが、POSIXコマンドの置換によれば、ヌルバイト 1(標準入力がテキストでない場合)は解決しないことに注意してください。

出力にnullバイトが含まれている場合の動作は規定されていません。

ただし、シェルの実装ではnullバイトが保持される場合があります。


通常、テキストファイルにはnullバイトが含まれますか?なぜそうなるのか分かりません。しかし、今述べたスクリプトは機能していないようです。
CommaToast 2014

@CommaToastテキストファイルにnullバイトが含まれていません。しかし、質問はstdin / input streamを言っているだけで、これは最も一般的なケースではテキストではない可能性があります。
vinc17 2014

OK。コマンドラインから試したところ、何も実行されませんでした。また、スクリプト自体からは、ファイルの最後に "..."が追加されているため、提案は失敗します。また、そこに改行がない場合でも、1つ追加されます。
CommaToast 2014

@CommaToast「...」は単なる例です。私は私の答えを明確にしました。改行は追加されません(例の「...」ののテキストを参照してください)。
vinc17 2014

1
まあ、シェルは物事を隠すべきではない、それはクールではない。それらの砲弾は発砲されるべきです。私のコンピュータが自分よりもよく知っているとコンピュータが思っているときは、私はそれが好きではありません。
CommaToast

4

readビルトインを使用してこれを達成できます:

$ IFS='' read -d '' -r foo < <(echo bar)

$ echo "<$foo>"
<bar
>

スクリプトがSTDINを読み取るには、次のようにします。

IFS='' read -d '' -r foo

 

これがどのシェルで機能するかはわかりません。ただし、bashとzshの両方で正常に動作します。


-dまた、プロセス置換(<(...))も移植可能ではありません。このコードはdash、たとえばでは機能しません。
chepner 2014

ええと、プロセスの置換は答えの一部ではありません。それが機能することを示す例の一部にすぎませんでした。に関しては-d、私が免責事項を一番下に置いた理由です。OPはシェルを指定しません。
Patrick

@chepner-スタイルは少し異なりますが、コンセプトは確かにで機能しdashます。他のシェルがパイプをプロセス置換に使用するのと同じ方法で、ヒアドキュメントにパイプを使用<<HEREDOC\n$(gen input)\nHEREDOC\nするdash-を使用するだけで、違いはありません。read -d事はただの区切り文字を指定している-あなたは、同じAのダースの方法を行うことができます-ちょうどそれについて確認してください。あなたはいくつかの尾が必要になりますがgen input
mikeserv 2014

あなたはIFS = ''を設定するので、それはそれがそれで読む行の間にスペースを入れませんか?クールなトリック。
CommaToast 14

実際には、この場合はIFS=''おそらく必要ありません。readスペースが崩れないようにするためです。しかし、それが単一の変数に読み込んでいるときは、影響はありません(思い出すことができます)。しかし、私はそれをそのままにしておくほうが安全だと感じています:-)
Patrick

2

あなたは次のようにすることができます:

input | { var=$(sed '$s/$/./'); var=${var%.}; }

とにかく$varその{ current shell ; }グループの外に出るとすぐに、何をしても消えます。ただし、次のように機能することもできます。

var=$(input | sed '$s/$/./'); var=${var%.}

1
最初の溶液を用いて、すなわち使用したことに留意すべき$var{ ... }、グループ化することは常に可能ではありません。たとえば、このコマンドがループの内側で実行され、ループの$var外側で必要な場合です。
vinc17 2014

@ vinc17 -それがループである場合、私は、私の代わりにそれを使用することになり、使用することが望ま{}.ITが真の括弧- 明示的な答えに注目される -の値があること$varである可能性が非常に高いとき、完全に消えて{ current shell; }グループ化があります閉まっている。いくつかありますより多くのよりも、それを言うための明示的な方法は、何をするにしても$var消滅するが...?
mikeserv 2014

@ vinc17-ただし、おそらく最良の方法:input | sed "s/'"'/&"&"&/g;s/.*/process2 '"'-> &'/" | sh
mikeserv

1
コマンド置換の結果をグローバル変数に保存するの_variables関数もありbash_completionますCOMPREPLY。改行を維持するためにパイプラインソリューションが使用された場合、結果は失われます。あなたの答えでは、どちらのソリューションも同等に良いという印象を持っています。さらに、パイプラインソリューションの動作はシェルに大きく依存していることに注意してください。ユーザーはecho foo | { var=$(sed '$s/$/./'); var=${var%.}; } ; echo $varksh93とzshでテストでき、このコードにはバグがありますが、問題ないと考えています。
vinc17 09/10/14

1
「動かない」とは言わなかった。「$var消える」と言っただけです(これはシェルに依存するため、実際には当てはまりません。動作はPOSIXで指定されていません)。これはかなり中立的な文です。2番目のソリューションは、この問題の影響を受けず、すべてのPOSIXシェルで動作が一貫しているため、より優れています。
vinc17 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.