ksh bashとzshをダッシュ​​するためにデフォルトのコマンドを移植する方法はありますか?

ksh bashとzshをダッシュ​​するためにデフォルトのコマンドを移植する方法はありますか?

ダッシュ(およびbash zshや他のシェル)でlocalこのコマンドが実行することは、変数の範囲を対応する関数(および場合によってはサブ関数)に制限することです。これにより、変数をこの関数に制限できます。内部構造のみ(場合によってはサブ関数呼び出し)。

たとえば、

testlocal(){ 
                local IFS
                IFS=123
                echo "internal  IFS = $IFS"
                testdescend
}

testdescend(){
                echo "descended IFS = $IFS"
}

IFS=abc
testlocal
echo "external  IFS = $IFS"

ダッシュ(bashとzshを含む)で実行すると、次の出力が生成されます。

$ dash ./script
internal  IFS = 123
descended IFS = 123
external  IFS = abc

これは、IFSがまだ存在することを意味します。地元の関数(および子孫)へ。

ただし、kshは異なり、次は許可されませんlocal

$ ksh ./script
./mt2[3]: local: not found [No such file or directory]

「local」という単語を「typeset」に変更すると、スクリプトはkshでほとんど実行可能ですが、dashでは認識されませんtypeset

functionそして、kshでスコープを動的にするには(呼び出された関数まで)、関数は次の単語を使用して定義する必要があります。

function testlocal { 
                     typeset IFS
                     IFS=123
                     echo "internal IFS = $IFS"
                     testdescend
                   }

これはダッシュの移植性をより複雑にする。

問題は、元のスクリプトがkshでも機能するようにするにはどうすればよいですか?どのシェルがスクリプトを実行しているかをテストするのを避けることはできますか?

ベストアンサー1

localksh88とすべてのクローンはダイナミックスコープを実行し、少なくとも1990年にはksh88と1994年pdksh(そしてbash多くのksh88 APIが実装された1989年(現在))ksh88以来これをサポートしてきました。

あなたがksh言及しているのは、ksh93少し異なり、互換性のないAPIを持つDavid Kornが最初から新しく実装したものです。

数十年のAPI(ksh93実装を除くすべての実装)を参照するksh93よりも、このシェルをAPIと呼ぶ方が良いです。 2000年から数年が経ってから、そのコードがオープンソースとして公開され、広く使われてきました。kshkshksh88ksh93

ksh93のtypeset唯一の機能変化のない範囲指定。 POSIXはダイナミックにスコープされているため、ksh88のtypeset/指定は使用されなくなりましたlocal(Cなどのほとんどの言語がこれを実行しても)。変化のない範囲指定、動的スコープの指定は、サブシェルまたは環境を介して得られる効果なので、シェルでより自然です。これは、ksh93が静的に書き換えられた理由を説明します。

後で他のほとんどのシェルはksh88のスコープを実装したので、ksh93は例外です。皮肉なことに、POSIXの唯一の合理的なオプションは、ダイナミックレンジを指定することです。 David Kornは最初はksh93で動的スコープを実装することを拒否しましたが、localPOSIXメーリングリストで/ buildinキーワードを使用することを検討できると述べましたが、これが完全に実装される前に引退しました。

AT&Tのksh93v-betaおよび最終バージョンは、実験的な「bash」モード(実際にはデフォルトで有効)を使用してコンパイルできます。このモードは、で呼び出されたときに動的スコープ(withlocalやインクルードを含む関数形式typeset)を実行します。ksh93bashbashこのモードは ksh2020 ではデフォルトで無効になっています。しかし、local/ aliasは、bashモードがコンパイルされていない場合でもdeclare維持されます。typeset(まだ静的範囲がありますが)。

今ベータ版とbashモードを取り除くと、ksh93ただkshスタイルの構文()を使用して、宣言された関数内でのみ静的スコープを実行しますfunction name { code; }。 Bourneスタイルの構文()を使用して宣言された関数がf() command範囲を適用しないため、混乱しています。別の言葉。これは、呼び出されたソースファイルまたはksh関数を使用するのと同じです. name [args]。ここではtypeset、関数のスコープに新しい変数が宣言されず(関数にはスコープがない)、現在のスコープの変数タイプのみが更新され、変数タイプはグローバルスコープまたはkshのスコープになります。 -style 関数は、Bourne スタイルが (最終的に) ksh スタイル関数で呼び出される場合です。

Bourneスタイル関数のコードは、呼び出し時に挿入/コピー - 貼り付け/ソースのように実行されます。

var=global
function ksh_function {
  typeset var=private
  echo "ksh1: $var"
  bourne_function
  echo "ksh2: $var"
  other_ksh_function other
  echo "ksh3: $var"
  . other_ksh_function other_invoked_with_dot
  echo "ksh4: $var"
}
bourne_function() {
  typeset var=set-from-bourne-function
}
function other_ksh_function {
  echo "other: $var"
  var=$1
}
ksh_function
echo "global: $var"

以下を提供します。

ksh1: private
ksh2: set-from-bourne-function
other: global
ksh3: set-from-bourne-function
other: set-from-bourne-function
ksh4: other_invoked_with_dot
global: other

ksh93では、次のようにサブシェルを使用したり、変数スタックを直接実装したりする以外に、ダイナミックスコープを指定することはできません。locvar概念の証拠またはあなた出口すべてのコマンドに渡される変数(環境を介した外部コマンドを含むkshスタイル関数を使用して宣言された関数を含む)

testlocal関数にのみ(ではなく)ローカルスコープが必要な場合は、説明されているtestdescendようにshdef+メソッドを使用できます。kshdefそこまたは、次のようにします。

case $KSH_VERSION in
  (*" 93"*)
    fn_with_local_scope() {
      alias local=typeset
      eval "function $1 {
        $(cat)
      }"
    }
  ;;
  (*)
    fn_with_local_scope() {
      eval "$1() {
        $(cat)
      }"
    }
  ;;
esac

次に、関数を次のように宣言します。

fn_with_local_scope testlocal << '}'
  local IFS
  IFS=123
  echo "internal IFS = $IFS"
  testdescend
}

testdescend(){
  echo "descended IFS = $IFS"
}

IFS=abc
testlocal
echo "external IFS = $IFS"

(そしてlocal宣言された関数内でのみfn_with_local_scope)。

これは作る

internal IFS = 123
descended IFS = 123
external IFS = abc

すべてのシェルで(サポートには最新バージョンyash(2.48以降)が必要ですlocal。)

または、ローカル変数のエクスポートに同意する場合(ksh93のみ):

case $KSH_VERSION in
  (*" 93"*)
    fn() {
      alias local='typeset -x'
      eval "function $1 {
        $(cat)
      }"
    }
  ;;
  (*)
    fn() {
      eval "$1() {
        $(cat)
      }"
    }
  ;;
esac

fn testlocal << '}'
  local IFS
  IFS=123
  echo "internal IFS = $IFS"
  testdescend
}

fn testdescend << '}'
  echo "descended IFS = $IFS"
}

IFS=abc
testlocal
echo "external IFS = $IFS"

次に、Cやksh93などの静的範囲の言語を使用して同様の操作を実行するには、次のようにします。

function testlocal {
  typeset IFS
  IFS=123
  echo "internal IFS = $IFS"
  testdescend "$IFS"
}

function testdescend {
  typeset IFS="$1" # explicitly get the value $IFS from the caller
  echo "descended IFS = $IFS"
}

私の考えでは、これはより良いデザインであり、コードはダイナミックスコープを実行するシェルでうまく機能します(他の関数定義構文を解決する必要があります)。

追加資料:

おすすめ記事