How to prevent ksh on Linux to overwrite a global varibale by a local variable?
I am involved in the process of porting a system containing several ksh scripts from AIX 6.1 to SUSE-Linux. I have come across the following difference in the way ksh behaves on the two systems:
# LocalVar.sh
test_loc_var()
{
typeset -t var
var=localvariable
echo "var = $var"
}
typeset var=globalvariable
echo "var = $var"
test_loc_var
echo "var = $var"
The correct result on AIX is:
var = globalvariable
var = localvariable
var = globalvariable
The wrong result on Linux is:
var = globalvariable
var = localvariable
var = localvariable
My questions are:
Note:
The following table summarizes the two systems:
uname -s | Linux AIX
uname -r | 2.6.16.60-0.54.5-smp 1
which ksh | /bin/ksh /usr/bin/ksh
rpm -qa | grep -i ksh | ksh-93s-59.11.35 -
lslpp -l | grep -i ksh | - bos.rte.shell 6.1.8.15 APPLIED Shells (bsh, ksh, csh)
TL;DR : For trivial cases: switch your function definition syntax from f() compound-command
to function f { ...; }
function f { ...; }
. For complex cases: Depend on ksh93-only (much more flexible), use the below absurd hacks (hard), rewrite to be strictly POSIX conforming (maybe hard, inflexible), rewrite in a real language (but shells are nice sometimes).
There is no "Linux ksh". It behaves the same on all systems and only depends on the version you're using.
AIX ships a modified ksh88. ksh88 had a dynamic scope system, similar to Bash and all other shells that support locals, but unlike ksh93. In order for locals to work under ksh93, you must use the "modern" function name { ; }
function name { ; }
syntax, rather than the POSIX syntax to define functions. This may or may not be required in ksh88, as it isn't documented and there is no possible way for me to test, as ksh88 is proprietary software and most likely isn't even built to run on modern x86 hardware.
If the above is correct, and your scripts were written for ksh88, simply switching function definition syntax is enough for local variables to at least function. However, While ksh93's static scope is vastly superior to the dynamic scope of other shells, it does cause a serious portability problem -- probably one of the most difficult to work around in all of shell scripting.
If you need portable locals, there are no fantastic solutions. I've come up with two techniques which "break" ksh scope to be more like ksh88/bash/mksh/zsh etc.
The first works in non-broken POSIX shells.
#!/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"
The second method only works in ksh-like shells and involves explicitly passing everything by reference and using indirection extensively.
#!/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
I only recommend either of these if you're one of the few that requires writing robust library code that has to be portable too.
链接地址: http://www.djcxy.com/p/70254.html