ダッシュ(および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
local
ksh88とすべてのクローンはダイナミックスコープを実行し、少なくとも1990年にはksh88と1994年pdksh
(そしてbash
多くのksh88 APIが実装された1989年(現在))ksh88以来これをサポートしてきました。
あなたがksh
言及しているのは、ksh93
少し異なり、互換性のないAPIを持つDavid Kornが最初から新しく実装したものです。
数十年のAPI(ksh93実装を除くすべての実装)を参照するksh93
よりも、このシェルをAPIと呼ぶ方が良いです。 2000年から数年が経ってから、そのコードがオープンソースとして公開され、広く使われてきました。ksh
ksh
ksh88
ksh93
ksh93のtypeset
唯一の機能変化のない範囲指定。 POSIXはダイナミックにスコープされているため、ksh88のtypeset
/指定は使用されなくなりましたlocal
(Cなどのほとんどの言語がこれを実行しても)。変化のない範囲指定、動的スコープの指定は、サブシェルまたは環境を介して得られる効果なので、シェルでより自然です。これは、ksh93が静的に書き換えられた理由を説明します。
後で他のほとんどのシェルはksh88のスコープを実装したので、ksh93は例外です。皮肉なことに、POSIXの唯一の合理的なオプションは、ダイナミックレンジを指定することです。 David Kornは最初はksh93で動的スコープを実装することを拒否しましたが、local
POSIXメーリングリストで/ buildinキーワードを使用することを検討できると述べましたが、これが完全に実装される前に引退しました。
AT&Tのksh93v-betaおよび最終バージョンは、実験的な「bash」モード(実際にはデフォルトで有効)を使用してコンパイルできます。このモードは、で呼び出されたときに動的スコープ(withlocal
やインクルードを含む関数形式typeset
)を実行します。ksh93
bash
bash
このモードは 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"
}
私の考えでは、これはより良いデザインであり、コードはダイナミックスコープを実行するシェルでうまく機能します(他の関数定義構文を解決する必要があります)。
追加資料:
答え2
私はbash(私の意見ではより良い配列構文)を好むが、時にはkshスクリプトを実行するので、便利な場合は次のことをしようとしています。
常にfunction func_name { body; }
構文を使用してください。
上記に基づいて、local
最初の関数の前にキーワード:を使用するには、次のようにします。
# teach ksh 93 about local
case "$KSH_VERSION" in *' 93'*) alias local='typeset -x' ;; esac
関数内で関数名を取得するには、次のようにします。
local MYNAME="${FUNCNAME[0]:-$0}"