bashの関数参照を介してstdoutをキャプチャし、変数を変更する必要があります。

bashの関数参照を介してstdoutをキャプチャし、変数を変更する必要があります。

(サブシェルの)関数呼び出しで変数としてstdoutを簡単にキャプチャできます。

val="$(get_value)"

シェルで変数(配列など)を変更することもできます。引用する同じシェルで次のようにしましょう。

function array.delete_by_index {
    local array_name="$1"
    local key="$2"

    unset "$array_name[$key]"
    eval "$array_name=(\"\${$array_name[@]}\")"
}

array.delete_by_index "array1" 0

しかし、私は両方をきちんとした方法で行う方法を見つけようとしています。私が望む例は、配列から値をポップすることです。

function array.pop {
    local array_name="$1"

    local last_index=$(( $(eval "echo \${#$array_name[@]}") - 1 ))
    local tmp="$array_name[\"$last_index\"]"
    echo "${!tmp}"

    # Changes "$array_name" here, but not in caller since this is a sub shell
    array.delete_by_index "$array_name" $last_index
}

val="$(array.pop "array1")"

stdoutを変数としてキャプチャするすべての型はbashのサブシェルを必要とし、サブシェルを使用すると呼び出し元のコンテキストから参照に値を変更できないようです。

これを達成するための魔法のバシズムを知っている人がいるのだろうか。私は特にファイルシステムで何らかのファイル/fifoを使用するソリューションを望んでいません。

2番目の答えこの質問にはこの設定は明らかに出力キャプチャを許可しますが、サブシェルを使用することは許可されていないため、これは可能であるkshことを示すようです。val="${ cmd; }"はい、技術的にはkshに切り替えることができますが、bashでこれが可能かどうか疑問に思います。

答え1

これはbash(バージョン4.3以降)動作し、ksh93「bashify」するには、グローバルスコープtypesetのすべての機能を(すべてのオプションを維持しながら!)に置き換えます。正直言って、なぜBashがそのようなものに興味を持っているのかわかりません。localtypesetdeclaretypeset

function stack_push
{
    typeset -n _stack="$1"
    typeset element="$2"

    _stack+=("$element")
}

function stack_pop
{
    typeset -n _stack="$1"
    typeset -n _retvar="$2"

    _retvar="${_stack[-1]}"

    unset _stack[-1]
}

typeset -a stack=()

stack_push stack "hello"
stack_push stack "world"

stack_pop stack value
printf '%s ' "$value"

stack_pop stack value
printf '%s\n' "$value"

これは関数でnamerefを使用することで回避できますeval(私はevalどのスクリプトでもこれを使用したことがありません!)。関数にポップされた値を保存する場所を指定すると、stack_popサブシェルの使用を回避できます。サブシェルを避けることで、関数はstack_pop外部範囲の変数値を変更できます。stack

関数内のローカル変数の下線は、namerefが参照する変数と同じ名前を持つことを防ぐことです(Bashはそれを気にしません。気にしません)kshこの問題)。

ksh次のように関数を作成できます。stack_pop

function stack_pop
{
    typeset -n _stack="$1"

    printf '%s' "${_stack[-1]}"

    unset _stack[-1]
}

そして電話してください

printf '%s %s\n' "${ stack_pop stack }" "${ stack_pop stack }"

${ ... }と同じ$( ... )ですが、サブシェルを生成しません)

しかし、私はこれがあまり好きではありません。 IMHOデータをstdoutに送信する必要はなく、データを取得するstack_popために呼び出す必要もありません。${ ... }私は元のものを好み、必要に応じて上記のタスクを実行するstack_popことを追加できます。stack_pop_print

stack_popBashの場合は、投稿の先頭でを使用してからスタックの最上位要素を削除せずにstdoutに印刷することができます(サブシェルで実行されるstack_top_print可能性が高いため削除できません)。$( ... )

答え2

ファイルを使用しない、または関数を変更しないことについて私が考えることができる唯一の解決策は、コマンドを2回実行することです。 1 つは子シェルからキャプチャし、もう 1 つは親シェルで変数を変更します。

val="$(array.pop "array1")""
配列。ポップ配列1

関数をオーバーライドするオプションがある場合:

関数 array.pop {
    ローカル配列名= "$ 1";
    Local last_index=$(( $(eval "echo \${#$array_name[@]}") - 1 ));
    ローカル tmp="$array_name[\"$last_index\"]";
    エコー "${!tmp}";

    もし[[$ 2]];
        eval "$2=\"${!tmp}\"" # ここに魔法攻撃があります。
    フィリピン諸島

    array.delete_by_index "$array_name" "$last_index";
}

次に実行すると、array.pop 'array1' variablename変数が存在しなくても変数が初期化されます。

$array1=(1 つまたは 2 つ)
$array.pop array1 var
サム
$echo "$var"
サム
$ echo "${array1[*]}"
1つまたは2つ

関連情報