文字列を返し、リダイレクトと出力を理解します。

文字列を返し、リダイレクトと出力を理解します。

端末に複数行を印刷する必要があるbash関数から文字列を選択的に出力するためにリダイレクトを使用する方法を理解しようとしています。私は答えを見つけましたStackOverflowに関する質問。以下のコードは機能します。しかし、私は方法や理由を理解していません。

#!/bin/bash

exec 3>&1

returnString()
{
    exec 4>&1 >&3
    local s=$1
    s=${s:="some default string"}
    echo "writing to stdout"
    echo "writing to stderr" >&2
    exec >&4-
    echo "$s"
}

my_string=$(returnString "$*")
echo "my_string:  [$my_string]"

stdout私はfd3が最初にリダイレクトされることを知っています。それからfd4も同様です。 1は3を指し、基本的に自分を指します。しかし、これが端末に印刷される内容にどのような影響を与えますか?この出力はには表示されませんが、$my_stringfd4を削除した後に最後の出力に表示されるのはなぜですか?

fd4に明示的に送信されたものがないため、fd4がどのようなものに関連しているのかわかりません。

答え1

上記の解決策は一般的な解決策であり、多くの場合を網羅しています。問題があることを知らない限り、YAGNI(不要)の姿勢をとり、選択した行を/ dev / ttyにリダイレクトできます。

これを防ぐには、現在発生しているファイル記述子(fd)ジャグリングを理解する必要があります。

3>&1関数が呼び出しコンテキストの標準出力に出力できるように、呼び出しコンテキストの標準出力を「保存単位」にコピーします。この場合は/ dev / ttyです。この関数は、${} 代替サブシェルに属する stdout を使用して呼び出されることに注意してください。

exec 4>&1関数の標準出力であるfd1のコピーを作成します。 fd4は、関数の開始時に標準出力の保持単位として使用されます。>&3関数/サブシェルの標準出力を呼び出し元の標準出力に設定します。したがって、すべての出力は呼び出し元の標準出力に送信されます。 UNTILは最後に>&4-保存された標準出力を開始位置に戻し、最後のecho "$s"標準出力を関数(およびサブシェル)の標準出力に含めることができます。

うわー!

and は and でより明確に書くことも>&3できます。>&4-1>&31>&4-

リダイレクトに関するマニュアルセクションでは、bashすべての詳細と用語について説明します。私は{name}形式を使って実際に数字を単語に置き換えることができることを知っていてとても嬉しかったです。

「ファイル記述子番号で始まる可能性のある各リダイレクトは、{varname}形式の単語で始めることができます。この場合、>&-および<&-を除く各リダイレクト演算子について、10より大きいシェルAファイル記述子は次のようになります。 varname に割り当てられます。 >&- または <&- の前に {varname} がある場合、varname の値は閉じるファイル記述子を定義します。

私はこれをテストしていませんが、次のようにコーディングできることを提案します。

#!/bin/bash

exec {caller_stdio}>&1

returnString() {
  exec {func_stdio}>&1 1>&{caller_stdio}
  local s=$1
  s=${s:="some default string"}
  echo "writing to stdout"
  echo "writing to stderr" >&2
  exec 1>&{func_stdio}-
  echo "$s"
}

my_string=$(returnString "$*")
echo "my_string:  [$my_string]"

答え2

exec 3>&1

fd3は今です繰り返すfd 1(あなたの例では端末)。マニュアルページに記載されているように、これはfd 1とfd 3が同じファイルまたはデバイスを参照するために交換可能に使用できることを意味します。

シェルから複製する主な目的は、後で復元できるようにfdのコピーを保存することです。

my_string=$(returnString "$*")

Bashがの評価から標準出力を得るためにreturnString "$*"管路そしてフォークを持って子供が叫んだ。デュープ2到着移動するパイプの書き込み終了(この例ではfd 5ですが異なる場合があります)をfd 1として指定します。

exec 4>&1 >&3

リダイレクトは左から右に評価されます。 fd 4 はパイプ端の繰り返しに設定されます。そして、fd 1 は端末を参照するfdのコピーに設定される。

echo "writing to stdout"

端末であるfd 1にエコーされます。

exec >&4-

これにより、fd 4がfd 1に移動されます(つまり、dup2の実行)。 fd 1は今パイプの終わりです。

echo "$s"

エコーはパイプの端に達します。最終的に、親シェルはパイプのもう一方の端からこの値を読み取り、次のように使用します。$(returnString "$*")

関連情報