複数行の出力をBash変数にキャプチャする


583

以下を出力するスクリプト 'myscript'があります。

abc
def
ghi

別のスクリプトでは、私は次のように呼びます。

declare RESULT=$(./myscript)

$RESULT値を取得します

abc def ghi

結果を改行または '\ n'文字で保存して ' echo -e'で出力できるようにする方法はありますか?


1
それは私を驚かせます。$(cat ./myscipt)はありませんか?それ以外の場合は、コマンドabc、def、ghiを実行しようとすると思っていました
Johannes Schaub-litb

@litb:はい、そう思います。コマンドの実行を回避する$(<./ myscript)を使用することもできます。
ジョナサンレフラー

2
(注:上記の2つのコメントは、開始した質問のリビジョンを参照しています。次のを含むスクリプト 'myscript'があり、それが質問につながりました。質問の現在のリビジョン(私はスクリプト '以下が出力のMyScriptは」)のコメントは不要になりしかし、改正は、2件のコメントがなされたずっと後、2011年11月11日からである。。
ジョナサン・レフラー

回答:


1083

実際、RESULTには必要なものが含まれています。

echo "$RESULT"

あなたが示すものはあなたが得るものです:

echo $RESULT

コメントに記載されているように、違いは、(1)変数の二重引用符付きバージョン(echo "$RESULT")は、変数で表されているとおりの値の内部間隔(改行、タブ、複数の空白、およびすべて)を保持しますが、(2 )引用符で囲まれていないバージョン(echo $RESULT)は、1つ以上の空白、タブ、改行の各シーケンスを1つのスペースに置き換えます。したがって、(1)は入力変数の形状を保持しますが、(2)単一のスペースで区切られた「単語」を持つ非常に長い出力の単一行を作成します(「単語」は空白以外の文字のシーケンスです。単語に英数字を使用しないでください)。


69
@troelskn:違いは、(1)変数の二重引用符で囲まれたバージョンは、変数、改行、タブ、複数の空白、およびすべてで表される値の正確な内部間隔を維持するのに対し、(2)引用符で囲まれていないバージョンは、 1つ以上の空白、タブ、改行の各シーケンスと単一のスペース。したがって、(1)は入力変数の形状を保持しますが、(2)単一のスペースで区切られた「単語」を持つ非常に長い出力の単一行を作成します(「単語」は空白以外の文字のシーケンスです。単語に英数字を使用しないでください)。
ジョナサンレフラー

23
答えをわかりやすくするために、echo "$ RESULT"は改行を保持しますが、echo $ RESULTは保持しないと答えます。
ゆうシェン

これは、状況によっては改行と先頭のスペースを維持できない。
CommaToast

3
@CommaToast:詳しく説明しますか?末尾の改行は失われます。それを回避する簡単な方法はありません。先頭の空白—私はそれらが失われる状況を知りません。
ジョナサンレフラー


90

これのもう1つの落とし穴は、そのコマンド置換$()—末尾の改行を削除することです。おそらく常に重要であるとは限りませんが、出力されたものを正確に保持したい場合は、別の行と引用符を使用する必要があります。

RESULTX="$(./myscript; echo x)"
RESULT="${RESULTX%x}"

これは、考えられるすべてのファイル名処理する場合に特に重要です(誤ったファイルでの操作などの未定義の動作を回避するため)。


4
私はしばらくの間、コマンド置換から最後の改行を削除しない(プロセス置換ではない )壊れたシェルで作業する必要がありましたが、ほとんどすべてが壊れていました。たとえば、の場合、の前に改行があり、名前を二重引用符で囲んでも役に立ちませんでした。それはすぐに修正されました。これはICL Perq PNXの1983-5年の時代にさかのぼります。シェルには組み込み変数がありませんでした。pwd=`pwd`; ls $pwd/$file/$PWD
Jonathan Leffler、

19

特定の行に関心がある場合は、result-arrayを使用します。

declare RESULT=($(./myscript))  # (..) = array
echo "First line: ${RESULT[0]}"
echo "Second line: ${RESULT[1]}"
echo "N-th line: ${RESULT[N]}"

3
行にスペースがある場合、行ではなくフィールド(スペース間の内容)がカウントされます。
リアム

2
readarrayコマンド置換の代わりに、置換を使用して処理しますreadarray -t RESULT < <(./myscript>
chepner

15

@ l0b0の回答に加えて、スクリプトが出力する後続の改行をすべて保持し、スクリプトの戻りコード確認する必要があるという状況がありました。そして、l0b0の答えの問題は、「エコーx」が$をリセットしていたということですか?ゼロに戻る...だから私はこの非常に狡猾な解決策を思いつくことができました:

RESULTX="$(./myscript; echo x$?)"
RETURNCODE=${RESULTX##*x}
RESULT="${RESULTX%x*}"

これは素敵です。実際にdie ()は、任意の終了コードとオプションでメッセージを受け入れる関数が必要なユースケースがあり、メッセージusage ()を提供するために別の関数を使用したかったのですが、改行がつぶれてしまいました。それを回避します。
dragon788 '25

1

ここでほとんどの解決策を試した後、私が見つけた最も簡単なことは明らかでした-一時ファイルの使用。複数行の出力をどのように処理するかはわかりませんが、readを使用して1行ずつ処理できます。あなたが実際に行うことができない唯一のことについては、すべてを同じ変数に簡単に固定することですが、ほとんどの実際的な目的では、これは扱いがはるかに簡単です。

./myscript.sh > /tmp/foo
while read line ; do 
    echo 'whatever you want to do with $line'
done < /tmp/foo

要求されたアクションを実行するためのクイックハック:

result=""
./myscript.sh > /tmp/foo
while read line ; do
  result="$result$line\n"
done < /tmp/foo
echo -e $result

これは余分な行を追加することに注意してください。あなたがそれに取り組むなら、あなたはそれをコードすることができます、私はただ怠惰です。


編集:このケースは完全にうまく機能しますが、これを読んでいる人は、whileループ内でstdinを簡単に押しつぶすことができるので、1行実行してstdinをクリアして終了するスクリプトを提供できることに注意してください。SSHのように私はそれを行うでしょうか?私は最近それを見ました、他のコード例はここにあります:https//unix.stackexchange.com/questions/24260/reading-lines-from-a-file-with-bash-for-vs-while

もう1回!今回は異なるファイルハンドルを使用します(stdin、stdout、stderrは0〜2なので、bashでは&3以上を使用できます)。

result=""
./test>/tmp/foo
while read line  <&3; do
    result="$result$line\n"
done 3</tmp/foo
echo -e $result

mktempを使用することもできますが、これは簡単なコード例です。mktempの使用法は次のようになります。

filenamevar=`mktemp /tmp/tempXXXXXX`
./test > $filenamevar

次に、実際のファイル名と同じように$ filenamevarを使用します。おそらくここで説明する必要はありませんが、誰かがコメントで不満を言っています。


私は他の解決策も試しましたが、最初の提案で私はようやくスクリプトを機能させました
Kar.ma

1
反対票:これは過度に複雑で、複数の一般的なbash落とし穴を回避できません。
tripleee 2017年

先日、誰かが奇妙なstdinファイルハンドルの問題について私に話しました、そして私は「すごい」のようでした。Lemmeは本当に素早く何かを追加します。
user1279741 2017年

バッシュでは、使用することができますreadコマンド拡張を-u 3ファイルディスクリプタ3から読み出すために
ジョナサン・レフラー

なぜしないのか...実行...完了<<(。myscript.sh)
jtgd

0

これはどうですか、それは変数に各行を読み取り、それを後で使用することができます!myscript出力がmyscript_outputというファイルにリダイレクトされると言う

awk '{while ( (getline var < "myscript_output") >0){print var;} close ("myscript_output");}'

4
まあ、それはbashではありません、それはawkです。
vadipp 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.