web-dev-qa-db-ja.com

サブシェルなしでシェル関数の出力をキャプチャする

私はrbenv(Rubyバージョンマネージャ)をマシンにインストールしており、次のように動作します。

_$ rbenv local
2.3.1
_

Rubyのローカルバージョンをstdoutに書き込みます。このバージョンを救出し、変数で宣言して別の機会で再利用したい。

_$ declare -r Ruby_DEFINED_VERSION=$(rbenv local)
$ echo Using Ruby version $Ruby_DEFINED_VERSION
Using Ruby version 2.3.1
_

できます!

しかし、私は($()または_``_を使用して)subshel​​lを使用して作業を行いたくありません。同じShellを使用したいのですが、tmpファイルを作成して作業を行いたくありません。

これを行う方法はありますか?

注: _declare -r_は必須ではなく、単純な_var=FOOBAR_でもかまいません。

7
dgmike

ハックはありますが、ループで必要な場合にのみ意味があると思います。

次のようにcatcoprocを開くことができます:coproc CAT { cat; }

これにより、catコマンドがバックグラウンドで開始され、2つの環境変数CAT_PIDおよびCATが設定されます。 CAT変数は、STDOUTによって使用されるSTDINおよびcat(この順序で)ファイル記述子(パイプ)を含む配列です。

したがって、STDINを表す&${CAT[1]}に出力を書き込むものを実行し、組み込みコマンドreadを使用して、変数である${CAT[0]}からの変数の読み取りを設定できます。猫のSTDOUT

ここにサンプル:

coproc CAT { cat; }
echo 123 >&${CAT[1]}
read myvar <&${CAT[0]}

テストする:

echo $myvar
123

使用後は必ず猫を止めてください。プロセスを強制終了することで実行できます。

kill $CAT_PID

これにより、パフォーマンスチューニングに大きな違いが生まれます。

7
ton

Bashを使用すると、次のようにすることもできます。

read a < <(echo hello)
echo "$a"

またはこのように:

shopt -s lastpipe
echo hello | read a
shopt -u lastpipe
echo "$a"

しかし、Rubyを実行するサブプロセスを起動する必要があるため、回避しようとしていることがよくわかりません...

3
Vouze

Linuxの場合、bashを使用すると次のことができます。

{
  chmod u+w /dev/fd/3 # only needed in bash 5+
  rbenv local > /dev/fd/3
  IFS= read -rd '' -u 3 variable
} 3<<< ''

それはあなたには隠されていますが、あらゆるhere-documentやhere-stringのような一時ファイルを使用します。

rbenvが出力するデータがブロックせずにパイプに収まらない場合(通常は64KiB)、LinuxとLinuxでのみ、次のように一時ファイルの代わりにパイプを使用できます。

{
  chmod u+w /dev/fd/3 # only needed in bash 5+
  rbenv local > /dev/fd/3
  IFS= read -rd '' -u 3 variable
} 3< <(:)

ksh93またはmkshの最近のバージョンでは、サブシェルを開始しないコマンド置換の形式を使用します。

variable=${
  rbenv local
}

IFS= read -rd ''とは逆に、出力の末尾の改行文字が削除されることに注意してください(すべてのコマンド置換と同様)。

1