TL;DR : 些細なケースの場合: 関数定義構文を からf() compound-command
に切り替えますfunction f { ...; }
。複雑なケースの場合: ksh93 のみに依存し (はるかに柔軟)、以下のばかげたハックを使用し (困難)、厳密に POSIX に準拠するように書き直します (おそらく困難で柔軟性がありません)、実際の言語で書き直します (ただし、シェルは場合によっては便利です)。
「Linux ksh」はありません。すべてのシステムで同じように動作し、使用しているバージョンにのみ依存します。
AIX は、変更された ksh88 を出荷します。ksh88 には動的スコープ システムがあり、Bash やローカルをサポートする他のすべてのシェルに似ていますが、ksh93 とは異なります。ローカルが ksh93 の下でfunction name { ; }
機能するには、関数を定義するために POSIX 構文ではなく、「最新の」構文を使用する必要があります。ksh88 は独自のソフトウェアであり、最新の x86 ハードウェアで実行するように構築されていない可能性が高いため、文書化されておらず、テストする方法がないため、これは ksh88 で必要な場合とそうでない場合があります。
上記が正しく、スクリプトが ksh88 用に作成されている場合、ローカル変数が少なくとも機能するには、関数定義構文を切り替えるだけで十分です。ただし、ksh93 の静的スコープは他のシェルの動的スコープよりもはるかに優れていますが、深刻な移植性の問題を引き起こします。これはおそらく、すべてのシェル スクリプトで回避するのが最も難しい問題の 1 つです。
移植可能なローカルが必要な場合、素晴らしい解決策はありません。kshスコープを「壊して」ksh88/bash/mksh/zshなどのようにする2つの手法を思いつきました。
最初のものは壊れていない POSIX シェルで動作します。
#!/bin/sh
# (Partially) Working shells: dash, posh, bash, ksh93v, mksh, older zsh
# Broken shells: current zsh, busybox sh, non-bleeding edge alpha ksh93, heirloom
f() {
if ! ${_called_f+false}; then
# Your code using "x"
for x; do
printf '%s, ' "$x"
done
else
# This hackishly localizes x to some degree
_called_f= x= command eval typeset +x x 2\>/dev/null \; f '"$@"'
fi
}
# demonstration code
x='outside f'; printf "$x, "; f 1 2 3; echo "$x"
2 番目の方法は、ksh のようなシェルでのみ機能し、参照によってすべてを明示的に渡し、インダイレクションを広範囲に使用します。
#!/usr/bin/env ksh
# bash, ksh93, mksh, zsh
# Breaking things for dash users is always a plus.
# This is crude. We're assuming "modern" shells only here.
${ZSH_VERSION+false} || emulate ksh
${BASH_VERSION+shopt -s lastpipe extglob}
unset -v is_{ksh93,mksh}
case ${!KSH_VERSION} in
.sh.version) is_ksh93= ;;
KSH_VERSION) is_mksh=
esac
function f {
# We want x to act like in dynamic scope shells. (not ksh93)
typeset x
g x
typeset -p x
}
function g {
# Note mksh and bash 4.3 namerefs kind of suck and are no better than eval.
# This makes a local of a pointer to the variable arg of the same name.
# Remember it's up to the programmer to ensure the sanity of any NAME
# passed through an argument.
${is_ksh93+eval typeset -n ${1}=\$1}
typeset y=yojo
# mksh... you fail at printf. We'll try our best anyway.
eval "$(printf %${is_mksh+.s%s=%s%.s }s=%q "$1" ${is_mksh+"${y@Q}"} "$y")"
}
f
移植性も必要な堅牢なライブラリ コードを記述する必要がある数少ないユーザーの 1 人である場合にのみ、これらのいずれかをお勧めします。