TL; DR: Dla błahych sprawach: przełączyć składnię definicji funkcji z f() compound-command
do function f { ...; }
. W przypadku złożonych przypadków: polegaj tylko na ksh93 (o wiele bardziej elastycznym), użyj poniższych absurdalnych hacków (twardych), przepisz, aby być ściśle zgodnym z POSIX (może twardym, nieelastycznym), przepisuj w prawdziwym języku (ale muszle są czasami miłe).
Nie ma "Linux ksh". Zachowuje się tak samo na wszystkich systemach i zależy tylko od wersji, z której korzystasz.
System AIX wysyła zmodyfikowaną wersję ksh88. ksh88 miał dynamiczny system zakresów, podobny do Basha i wszystkich innych powłok, które obsługują locals, ale w przeciwieństwie do ksh93. Aby locals działało pod ksh93, musisz użyć składni "nowoczesnej" function name { ; }
, a nie składni POSIX do definiowania funkcji. To może, ale nie musi być wymagane w ksh88, ponieważ nie jest ono udokumentowane i nie ma możliwości przetestowania go, ponieważ ksh88 jest prawnie zastrzeżonym oprogramowaniem i najprawdopodobniej nie jest zbudowany na nowoczesnym sprzęcie x86.
Jeśli powyższe jest poprawne, a twoje skrypty zostały napisane dla ksh88, wystarczy zmienić składnię definicji funkcji, aby przynajmniej funkcjonowały zmienne lokalne. Chociaż zakres statyczny ksh93 jest znacznie lepszy od zakresu dynamicznego innych powłok, powoduje to poważny problem przenośności - prawdopodobnie jeden z najtrudniejszych do obejścia we wszystkich skryptach powłoki.
Jeśli potrzebujesz przenośnych mieszkańców, nie ma fantastycznych rozwiązań. Wymyśliłem dwie techniki, które "łamią" zakres ksh, aby były bardziej podobne do ksh88/bash/mksh/zsh itd.
Pierwsze prace w niezłamanych powłokach 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 '"[email protected]"'
fi
}
# demonstration code
x='outside f'; printf "$x, "; f 1 2 3; echo "$x"
Druga metoda działa tylko w powłokach przypominających ksh i polega jawnie na przekazywaniu wszystkiego przez odniesienie i intensywne używanie kierunku pośredniego.
#!/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+"${[email protected]}"} "$y")"
}
f
Polecam tylko jedno z nich, jeśli jesteś jednym z niewielu, który wymaga napisania solidnego kodu biblioteki, który również musi być przenośny.
Witaj Ormaaj, dziękuję bardzo za szczegółową i BARDZO pomocną odpowiedź. Wydaje się, że miałem fałszywe oczekiwania i więcej do zrobienia, niż miałem nadzieję. Ale teraz wiem przynajmniej, jak postępować w nadchodzących problemach w procesie migracji. – Cologne2202