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

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

環境を変更するコマンドの出力を変数として保存するにはどうすればよいですか?

私は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

したがって、ここに環境の変化はありません。

関数だけでなく、コマンドの出力を変数に保存し、環境の変更を保存するにはどうすればよいですか?

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

答え1

Bash 4以降を使用している場合は、次のものを使用できます。共同プロセス:

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

coproccat与えられたコマンドを実行する新しいプロセス(ここ)を作成します。 PIDを配列に保存し、COPROC_PIDstdout / inputファイル記述子を配列に保存しますCOPROC(例:pipe(2)、または参照ここまたはここ)。

ここでは、実行中のcoprocessを指す標準出力で関数を実行しcatreadから。cat単に入力をエクスポートするだけなので、関数の出力を変数に入れます。永遠に待たないように exec {COPROC[1]}>&-ファイル記述子を閉じてください。cat


read一度に1行しか必要ありません。これを使用mapfileして行配列を取得したり、ファイル記述子を使用したりできますが、別の方法で使用したいと思います。

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


Series 3 バージョンの Bash を使用している場合は、次のコマンドを使用して同じ効果を得ることができますmkfifoしかし、この時点では実際のファイルを使用するよりもあまり良くありません。

答え2

すでに呼び出し元と同じシェルで本文を実行してなどの変数をf変更できると思っている場合は、関数を直接設定するのはどうですか?言い換えれば:abc

$ 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

答え3

クルーガーですが、一度試してください。

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

(およびオプションでrm c.file)。

答え4

すべての変数を割り当て、同時に出力を作成します。

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移植可能なコードです。パラメータ拡張は並行変数の割り当て+数値にのみ評価を制限するため、最初は空の文字列c=に設定しました。''(良い$((var=num))または null または存在しない値で - つまり、両方を設定できませんそして変数に値が割り当てられている場合、変数はランダムな文字列として評価されます。だから試してみる前に空であることを確認してください。割り当てを試みる前にクリアしないと、c拡張機能は古い値を返します。

証明するために:

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

newvalはいいいえ拡張がある$cため、割り当てはインラインで実行されますが、インライン算術割り当ては常に発生します。しかし、もしoldval${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()付き変数に出力を割り当て、他の点では。現在作成されているように、呼び出し元の範囲にvarを設定しないと機能しません。しかし、他の方法は次のとおりです。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

二重評価の場合、おそらく最もトリッキーなのは、変数が引用符を壊さずに任意のコードを実行しないことを確認することです。変数を評価するほど難しくなります。パラメータ拡張はここで大いに役立ち、export代わりに使用する方がeval安全です。

上記の例では、空の文字列がf()最初に割り当てられ、次に有効な変数名をテストするために位置パラメータが設定されています。両方のテストが失敗すると、メッセージが表示され、デフォルト値がに割り当てられます。ただし、テストに関係なく、指定した名前は常に割り当てられ、(オプションで)指定された名前は常に関数の出力値に割り当てられます。これを示すために、次のような小さなループを作成しました。$fout''stderrfout$fout_name$fout_namefout$foutfor

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'

関連情報