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

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

私のコンピュータに(Ruby Version Manager)をインストールしましたが、rbenv次のように動作します。

$ rbenv local
2.3.1

に書く標準出力私のローカルバージョンのRubyです。このバージョンを救出し、他の場合に再利用できるように変数に宣言したいと思います。

$ declare -r RUBY_DEFINED_VERSION=$(rbenv local)
$ echo Using ruby version $RUBY_DEFINED_VERSION
Using ruby version 2.3.1

効果がある!

しかし、使用したくないサブシェル作業を完了します($()または使用``)。私も同じように使いたいシェルtmp操作を完了するために必要なファイルです。

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

メモ: declare -r必須ではなく、簡単にすることができますvar=FOOBAR

答え1

ハッキングがありますが、ループを介して実行する必要がある場合は意味があると思います。

次のように開くことができますcat coproccoproc CAT { cat; }

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

&${CAT[1]}したがって、表示されるすべての項目に出力を書き込むことができ、STDIN組み込みコマンドを使用してread読み取る変数をcatに設定${CAT[0]}できますSTDOUT

例は次のとおりです。

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

テストするには:

echo $myvar
123

使用後は猫をブロックすることを忘れないでください。プロセスを終了したらこれを行うことができます。

kill $CAT_PID

これはパフォーマンスチューニングに大きな影響を与えます。

更新:区切り文字列をbash実装しましたnull。だからバイナリデータを扱うときはread本当に面倒です。一度に1バイトずつ読み取ることができ、nullは変数の空の文字列にLC_ALL=C read -r -n1 -d $'\0'なります。${REPLY}

答え2

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

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

または次のようになります。

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

しかし、まだRubyを実行するサブプロセスを開始する必要があるので、何を避けたいのかわかりません...

答え3

Linuxでbash5.1(またはzsh)以前のバージョンを使用している場合は、次のことができます。

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

非表示ですが、ここではドキュメントごと、またはここでは文字列などの一時ファイルを使用します。

bash5.1通常の一時ファイルの代わりにパイプを使用します(少なくともherefile / herestringの内容がパイプバッファに入るのに十分小さい場合)。これにより、いつでも一時ファイルを手動で作成できます。

tmpfile=$(mktemp) || exit
{
  rm -f -- "$tmpfile"
  rbenv local >&3
  IFS= read -rd '' -u4 variable
} 3> "$tmpfile" 4< "$tmpfile"

<<<<<(たとえば、シェルがシャットダウンしたときにスクリプトがシャットダウンまたは残されるリスクを最小限に抑えるために、tmpfileを早期に削除してください)。rbenv

出力がパイプに入ることができるよりも少ないデータ(通常64KiB)の場合は、rbenvまだLinuxとLinuxでのみ一時ファイルの代わりにパイプを使用できます。

{
  rbenv local > /dev/fd/3
  IFS= read -rd '' -u 3 variable
} 3< <(:)

ksh93または、最新バージョンの場合は、mkshサブシェルを起動しないコマンド代替形式を使用してください。

variable=${
  rbenv local
}

このバージョンは、現在のbashおよびzsh開発バージョンでも利用できます。

この方法とは異なり、IFS= read -rd ''出力から末尾の改行を削除します(通常のコマンド置換と同様)。

これを達成するために、ksh93の現在のバージョン(2024年)は、内部的に事前に削除された一時ファイル(/dev/shm私のDebianシステム上)、mksh(/tmp私のシステム上)、zsh-devを使用し、ファイルのみが後で削除されます。 bash-devを削除して匿名にするメモリサポートされている場所には一時ファイルがあり、サポートされていない場所には事前に削除された一時ファイルがあります/tmp

以前のバージョンでは、自動的に削除された一時ファイルをインポートするためにプロセス置換形式をzsh利用しました(ここでは匿名関数のパラメータとして渡されました)。=(...)

() { rbenv local > $1; IFS= read -rd '' variable <$1; } =()

1 しかし、zshに関する限り、${ cmd }現在これを行うか引用符なしで実行するかについての議論があります。

関連情報