環境を変更するコマンドの出力を変数に保存します


8

環境を変更するコマンドの出力を変数に保存する方法は?

私はbashシェルを使用しています。

私が持っていると仮定します:

function f () { a=3; b=4 ; echo "`date`: $a $b"; }

そして今、私はコマンドを使用して実行することができますf

$ a=0; b=0; f; echo $a; echo $b; echo $c
Sat Jun 28 21:27:08 CEST 2014: 3 4
3
4

の出力fを変数に保存したいcので、試してみました:

a=0; b=0; c=""; c=$(f); echo $a; echo $b; echo $c

しかし、残念ながら、私は持っています:

0
0
Sat Jun 28 21:28:03 CEST 2014: 3 4

ここでは環境の変更はありません。

コマンドの出力(機能だけでなく)を変数に保存し、環境の変更を保存する方法は?

私はそれ$(...)が新しいサブシェルを開き、それが問題であることを知っていますが、いくつかの回避策を実行することは可能ですか?


そう、問題はそれで$aあり$bf関数のローカル変数です。あなたはexportそれらをすることができますが、それは大ざっぱなようです。
HalosGhost 2014年

1
@HalosGhost:いいえ、そうは思いません。最初の例を見てください。…; f; echo $a; …結果は3エコーされるためf、シェル変数を変更します(それ自体のローカル変数だけではありません)。
スコット

回答:


2

Bash 4以降を使用している場合は、coprocessesを使用できます。

function f () { a=3; b=4 ; echo "`date`: $a $b"; }
coproc cat
f >&${COPROC[1]}
exec {COPROC[1]}>&-
read c <&${COPROC[0]}
echo a $a
echo b $b
echo c $c

出力されます

a 3
b 4
c Sun Jun 29 10:08:15 NZST 2014: 3 4

coproc指定されたコマンドを実行する新しいプロセスを作成します(ここでは、cat)。PIDを保存しCOPROC_PID、標準出力/入力ファイル記述子を配列に保存しますCOPROC(と同様pipe(2)、またはここまたはここを参照)。

ここでは、実行中のコプロセスに向けられた標準出力を使用して関数を実行し、catそれから関数を実行しますreadcat入力を吐き出すだけなので、関数の出力を変数に入れます。exec {COPROC[1]}>&-ファイル記述子を閉じるだけで、cat永久に待機し続けません。


readは一度に1行しか使用しないことに注意してください。を使用mapfileして行の配列を取得することも、ファイル記述子を使用することもできますが、別の方法で使用することもできます。

exec {COPROC[1]}>&-は現在のバージョンのBashで機能しますが、以前の4シリーズバージョンでは、最初にファイル記述子を単純な変数に保存する必要がありますfd=${COPROC[1]}; exec {fd}>&-。変数が設定されていない場合は、標準出力を閉じます。


3シリーズバージョンのBashを使用している場合は、で同じ効果を得ることができmkfifoますが、その時点で実際のファイルを使用するよりもはるかに優れています。


bash 3.1しか使用できません。パイプは使いにくいです。mktempは唯一のオプションですか?コマンドを実行して出力を取得するための特別な構文またはオプションはありませんが、環境が変更されていますか?
faramir 2014年

mkfifoを使用して、名前付きパイプをcoprocの代替として作成できます。これには、実際にデータをディスクに書き込む必要はありません。これにより、出力をfifoにリダイレクトし、コプロセスの代わりにそこから入力します。それ以外の場合は、いいえ。
Michael Homer

ファイルを使用しないように私の回答を適応させるための+1。しかし、私のテストでは、exec {COPROC[1]}>&-コマンドは(少なくとも時々)コプロセスをすぐに終了するため、その出力を読み取ることができなくなります。coproc { cat; sleep 1; }うまくいくようです。(1複数行を読んでいる場合は、を増やす必要があるかもしれません。)
スコット

1
ここで使用されている「実際のファイル」には、意図的に無視されている、よく知られた意味があります。
Michael Homer

1
「実際のファイル」とは、ディスク上のファイルを意味します。合理的な読者がそれらのいずれかが引数または環境変数と同じであると推測することを暗黙または許可したことはありません。私はこの議論を続けることに興味はありません。
Michael Homer

2

すでに本体にカウントしている場合はf、発信者、同じシェルで実行するため、などの変数を変更することができることab、その理由だけで設定されている機能しないcだけでなくとしての?言い換えると:

$ function f () { a=3; b=4 ; c=$(echo "$(date): $a $b"); }
$ a=0; b=0; f; echo $a; echo $b; echo $c
3
4
Mon Jun 30 14:32:00 EDT 2014: 3 4

考えられる理由の1つは、異なる場所で呼び出されたときに出力変数に異なる名前(c1、c2など)を付ける必要があることですが、関数をc_TEMPに設定し、呼び出し元に同じようにさせることで対処できますc1=$c_TEMP


1

クルージですが、試してみてください

f > c.file
c=$(cat c.file)

(オプションで、rm c.file)。


ありがとうございました。それは単純な答えであり、機能しますが、いくつかの欠点があります。を使用できることはわかっていますがmktemp、追加のファイルを作成したくありません。
faramir 2014年

1

すべての変数を割り当てて、同時に出力を書き込むだけです。

f() { c= ; echo "${c:=$(date): $((a=3)) $((b=4))}" ; }

今あなたがするなら:

f ; echo "$a $b $c"

あなたの出力は:

Tue Jul  1 04:58:17 PDT 2014: 3 4
3 4 Tue Jul  1 04:58:17 PDT 2014: 3 4

これは完全にPOSIXポータブルコードであることに注意してください。パラメータの展開により、同時変数割り当て+評価が数値のみまたはなどまたはnullまたは存在しない値からのいずれかに制限されるため、または変数を任意の文字列に同時に設定して評価できないためc=、最初は''null文字列に設定しましたその変数にすでに値が割り当てられている場合。だから私は試す前にそれが空であることを確認するだけです。割り当てようとする前に空にしないと、拡張は古い値のみを返します。$((var=num))c

ただ示すために:

sh -c '
    c=oldval a=1
    echo ${c:=newval} $((a=a+a))
'
###OUTPUT###
oldval 2

newvalはに展開されているため、インラインに割り当てられませんが、インライン算術割り当ては常に発生します。しかし、がなく、空または未設定の場合...$coldval${word}$((=))$c oldval

sh -c '
    c=oldval a=1
    echo ${c:=newval} $((a=a+a)) 
    c= a=$((a+a))
    echo ${c:=newval} $((a=a+a))
'
###OUTPUT###
oldval 2
newval 8

...次にnewval、に割り当てられ、に展開され$cます。

これを行う他のすべての方法には、何らかの形の二次評価が含まれます。たとえば、ある場所と別の場所でf()名前が付けられた変数に出力を割り当てたいとしましょう。現在書かれているように、これは呼び出し側のスコープで変数を設定しないと機能しません。ただし、別の方法では次のようになります。namevar

f(){ fout_name= fout= set -- "${1##[0-9]*}" "${1%%*[![:alnum:]]*}"
    (: ${2:?invalid or unspecified param - name set to fout}) || set --
    export "${fout_name:=${1:-fout}}=${fout:=$(date): $((a=${a:-50}+1)) $((b=${b:-100}-4))}"
    printf %s\\n "$fout"
}

f &&
    printf %s\\n \
        "$fout_name" \
        "$fout" \
        "$a" "$b"

以下でより適切にフォーマットされた例を提供しましたが、上記のように呼び出され、出力は次のとおりです。

sh: line 2: 2: invalid or unspecified param - name set to fout
Wed Jul  2 02:27:07 PDT 2014: 51 96
fout
Wed Jul  2 02:27:07 PDT 2014: 51 96
51
96

または、別の$ENVまたは引数を使用して:

b=9 f myvar &&
    printf %s\\n \
        "$fout_name" \
        "$fout" \
        "$myvar" \
        "$a" "$b"

###OUTPUT###

Tue Jul  1 19:56:42 PDT 2014: 52 5
myvar
Tue Jul  1 19:56:42 PDT 2014: 52 5
Tue Jul  1 19:56:42 PDT 2014: 52 5
52
5

おそらく、2回評価するときに正しく理解するのが最も難しいのは、変数が引用符を壊さず、ランダムコードを実行しないようにすることです。変数が評価される回数が増えるほど、変数は難しくなります。パラメータの拡張は、ここで非常に役立ちます。逆に使用exportする方evalがはるかに安全です。

上記の例では、f()最初$fout''null文字列を割り当て、次に位置パラメータを設定して有効な変数名をテストします。両方のテストに合格しない場合、メッセージが送信されstderr、デフォルト値のfoutがに割り当てられ$fout_nameます。かかわらず、テストの、しかし、$fout_name常にどちらかに割り当てられているfoutか、名前を指定し、$fout必要に応じて、あなたの指定した名前は、常に関数の出力値を割り当てられている、と。これを実証するために、この小さなforループを書きました。

for v in var '' "wr;\' ong"
    do sleep 10 &&
        a=${a:+$((a*2))} f "$v" || break
    echo "${v:-'' #null}" 
    printf '#\t"$%s" = '"'%s'\n" \
        a "$a" b "$b" \
        fout_name "$fout_name" \
        fout "$fout" \
        '(eval '\''echo "$'\''"$fout_name"\")' \
            "$(eval 'echo "$'"$fout_name"\")"
 done

変数名とパラメーター拡張を使用していくつかの機能を果たします。質問がある場合は質問してください。これは、すでにここで表されている関数の同じ数行のみを実行します。少なくとも、$a$b変数は、呼び出し時に定義されているか、すでに設定されているかによって動作が異なることに言及する価値があります。それでも、forはほとんど何もせず、データセットをフォーマットしてによって提供されf()ます。見てください:

###OUTPUT###

Wed Jul  2 02:50:17 PDT 2014: 51 96
var
#       "$a" = '51'
#       "$b" = '96'
#       "$fout_name" = 'var'
#       "$fout" = 'Wed Jul  2 02:50:17 PDT 2014: 51 96'
#       "$(eval 'echo "$'"$fout_name"\")" = 'Wed Jul  2 02:50:17 PDT 2014: 51 96'
sh: line 2: 2: invalid or unspecified param - name set to fout
Wed Jul  2 02:50:27 PDT 2014: 103 92
'' #null
#       "$a" = '103'
#       "$b" = '92'
#       "$fout_name" = 'fout'
#       "$fout" = 'Wed Jul  2 02:50:27 PDT 2014: 103 92'
#       "$(eval 'echo "$'"$fout_name"\")" = 'Wed Jul  2 02:50:27 PDT 2014: 103 92'
sh: line 2: 2: invalid or unspecified param - name set to fout
Wed Jul  2 02:50:37 PDT 2014: 207 88
wr;\' ong
#       "$a" = '207'
#       "$b" = '88'
#       "$fout_name" = 'fout'
#       "$fout" = 'Wed Jul  2 02:50:37 PDT 2014: 207 88'
#       "$(eval 'echo "$'"$fout_name"\")" = 'Wed Jul  2 02:50:37 PDT 2014: 207 88'
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.