मैं एक POSIX खोल समारोह जो स्थानीय स्तर पर एक खोल में निर्मित या में से किसी में समारोह नाम स्थान के लिए इस्तेमाल किया जा सकता है लिखा है ksh93, dash, mksh, या bash (विशेष रूप से नामित क्योंकि मैं व्यक्तिगत रूप से इन सभी में काम करने के लिए यह पुष्टि की है) । जिन गोले में मैंने इसका परीक्षण किया, वह केवल मेरी अपेक्षाओं को पूरा करने में विफल yashरहा, और मैंने कभी भी यह उम्मीद नहीं की कि मैं इसमें काम करूंगा zsh। मैंने परीक्षण नहीं किया posh। मैंने poshकुछ समय पहले कोई उम्मीद छोड़ दी है और इसे कुछ समय में स्थापित नहीं किया है। शायद यह में काम करता है posh...?
मैं कहता हूं कि यह POSIX है क्योंकि, विनिर्देश के मेरे पढ़ने से, यह एक बुनियादी उपयोगिता के निर्दिष्ट व्यवहार का लाभ उठाता है, लेकिन, वास्तव में, इस संबंध में विनिर्देश अस्पष्ट है, और, कम से कम एक व्यक्ति जाहिर तौर पर मुझसे असहमत है। आम तौर पर इस बात से मेरी असहमति रही है कि आखिरकार मुझे अपनी गलती मिल गई है, और संभवतः मैं इस बार विनिर्देश के बारे में भी गलत हूं, लेकिन जब मैंने उनसे सवाल किया तो उन्होंने कोई जवाब नहीं दिया।
जैसा कि मैंने कहा, हालांकि, यह निश्चित रूप से पूर्वोक्त गोले में काम करता है, और यह काम करता है, मूल रूप से, निम्नलिखित तरीके से:
some_fn(){ x=3; echo "$x"; }
x=
x=local command eval some_fn
echo "${x:-empty}"
3
empty
commandआदेश एक मूल रूप से उपलब्ध उपयोगिता और पूर्व में से एक के रूप में निर्दिष्ट किया जाता है $PATH'घ builtins। इसके निर्दिष्ट कार्यों में से एक यह है कि इन्हें बनाते समय अपने स्वयं के वातावरण में विशेष निर्मित उपयोगिताओं को लपेटना है, और इसलिए ...
{ sh -c ' x=5 set --; echo "$x"
x=6 command set --; echo "$x"
exec <""; echo uh_oh'
sh -c ' command exec <""; echo still here'
}
5
5
sh: 3: cannot open : No such file
sh: 1: cannot open : No such file
still here
... ऊपर दोनों कमांड-लाइन असाइनमेंट का व्यवहार कल्पना से सही है। दोनों त्रुटि स्थितियों का व्यवहार भी सही है, और वास्तव में विनिर्देशन से लगभग पूरी तरह से डुप्लिकेट है। वर्तमान शेल वातावरण को प्रभावित करने के लिए फ़ंक्शन या विशेष बिल्डिंस की कमांड-लाइनों के लिए उपसर्ग निर्दिष्ट हैं। इसी तरह, पुनर्निर्देशन त्रुटियों को घातक के रूप में निर्दिष्ट किया जाता है जब उन दोनों में से किसी पर भी इंगित किया जाता है। commandउन मामलों में विशेष निर्मितियों के विशेष उपचार को दबाने के लिए निर्दिष्ट किया जाता है, और पुनर्निर्देशन का मामला वास्तव में विनिर्देश में उदाहरण द्वारा प्रदर्शित होता है।
नियमित रूप से निर्मित, जैसे command, दूसरी ओर, एक उप-वातावरण में चलने के लिए निर्दिष्ट हैं - जो जरूरी नहीं कि एक और प्रक्रिया का मतलब है , बस यह कि यह एक से मौलिक रूप से अप्रभेद्य होना चाहिए। एक नियमित रूप से निर्मित कॉलिंग के परिणाम हमेशा एक समान सक्षम $PATHडी कमांड से प्राप्त किए जा सकते हैं । इसलिए...
na=not_applicable_to_read
na= read var1 na na var2 <<"" ; echo "$var1" "$na" "$var2"
word1 other words word2
word1 not_applicable_to_read word2
लेकिन commandकमांड शेल फ़ंक्शन को कॉल नहीं कर सकता है, और इसलिए इसका उपयोग उनके विशेष उपचार म्यूट को प्रस्तुत करने के लिए नहीं किया जा सकता है क्योंकि यह नियमित बिल्डरों के लिए हो सकता है। यह भी कल्पना है। वास्तव में, युक्ति कहती है कि इसकी एक प्राथमिक उपयोगिता commandयह है कि आप इसे किसी अन्य कमांड के लिए एक रैपर शेल फ़ंक्शन के भीतर उपयोग कर सकते हैं, जो कि अन्य कमांड को स्वयं-पुनरावृत्ति के बिना कॉल करने के लिए है क्योंकि यह फ़ंक्शन को कॉल नहीं करेगा। ऐशे ही:
cd(){ command cd -- "$1"; }
यदि आप commandवहाँ उपयोग नहीं करते हैं तो cdफ़ंक्शन निश्चित रूप से आत्म-पुनरावृत्ति के लिए segfault होगा।
लेकिन एक नियमित बिलिन के रूप में, जो विशेष निर्मित कॉल commandकर सकते हैं, एक उप-वातावरण में ऐसा कर सकते हैं । और हां, तो, जबकि वर्तमान खोल राज्य वर्तमान खोल करने के लिए हो सकता है छड़ी के भीतर परिभाषित - निश्चित रूप से readकी $var1और $var2था - कमांड लाइन परिभाषित करता है की कम से कम परिणाम शायद नहीं करना चाहिए ...
साधारण कमांड
यदि कोई कमांड नाम परिणाम नहीं देता है, या यदि कमांड नाम एक विशेष अंतर्निहित या फ़ंक्शन है, तो चर असाइनमेंट वर्तमान निष्पादन वातावरण को प्रभावित करेगा। अन्यथा, चर असाइनमेंट को कमांड के निष्पादन वातावरण के लिए निर्यात किया जाएगा और वर्तमान निष्पादन वातावरण को प्रभावित नहीं करेगा।
अब commandदोनों एक नियमित रूप से निर्मित होने और विशेष निर्मितियों को सीधे कॉल करने की क्षमता है या नहीं , बस कुछ अनपेक्षित कमियां हैं कमांड-लाइन परिभाषित के संबंध में मुझे नहीं पता है, लेकिन मुझे पता है कि कम से कम चार गोले पहले से ही हैं commandनेमस्पेस का सम्मान किया ।
और हालांकि commandसीधे शेल फ़ंक्शन को कॉल नहीं किया जा सकता है, इसे प्रदर्शन के रूप में कॉल कर सकते हैंeval , और इसलिए अप्रत्यक्ष रूप से ऐसा कर सकते हैं। इसलिए मैंने इस कॉन्सेप्ट पर नेमस्पेस रैपर बनाया। यह तर्कों की एक सूची लेता है जैसे:
ns any=assignments or otherwise=valid names which are not a command then all of its args
... सिवाय इसके कि commandऊपर के शब्द को केवल एक के रूप में पहचाना जाता है अगर इसे खाली के साथ पाया जा सकता है $PATH। इसके अलावा कमांड लाइन पर नामित खोल चर स्थानीय रूप से देखते हुए, यह भी इस तरह के रूप एकल लोअर केस वर्णमाला नाम और अन्य मानक वाले की एक सूची, के साथ सभी चर स्थानीय रूप से scopes $PS3, $PS4, $OPTARG, $OPTIND, $IFS, $PATH, $PWD, $OLDPWDऔर कुछ अन्य।
और हाँ, स्थानीय रूप से $PWDऔर $OLDPWDचर और बाद में स्पष्ट रूप से cdआईएनजी द्वारा $OLDPWDऔर $PWDयह काफी मज़बूती से वर्तमान कार्यशील निर्देशिका को भी गुंजाइश कर सकता है। इसकी गारंटी नहीं है, हालांकि यह बहुत कठिन प्रयास करता है। यह एक डिस्क्रिप्टर को बनाए रखता है 7<.और जब इसका रैप टार्गेट रिटर्न करता है cd -P /dev/fd/7/। यदि वर्तमान कार्यशील निर्देशिका unlink()अंतरिम रूप से घ में है, तो भी उसे कम से कम इसे वापस बदलने का प्रबंधन करना चाहिए, लेकिन उस मामले में एक बदसूरत त्रुटि का उत्सर्जन होगा। और क्योंकि यह विवरणक को बनाए रखता है, मुझे नहीं लगता कि एक केन कर्नेल को अपने रूट डिवाइस को या तो अनमाउंट किया जाना चाहिए (???) ।
यह स्थानीय स्तर पर शेल विकल्प भी देता है, और इनको उस स्थिति में पुनर्स्थापित करता है, जिसमें इसकी लिपटे उपयोगिता के वापस आने पर उन्हें पाया जाता है। यह $OPTSविशेष रूप से व्यवहार करता है कि यह अपने स्वयं के दायरे में एक प्रति रखता है जिसे वह शुरू में मान देता है $-। कमांड-लाइन पर सभी असाइनमेंट को संभालने के बाद यह set -$OPTSअपने रैप लक्ष्य को लागू करने से पहले करेगा । इस तरह यदि आप -$OPTSकमांड-लाइन पर परिभाषित करते हैं तो आप अपने रैप लक्ष्य के शेल विकल्पों को परिभाषित कर सकते हैं। जब लक्ष्य वापस आ जाता है तो set +$- -$OPTSइसकी अपनी प्रति $OPTS (जो कमांड-लाइन डिफाइन से प्रभावित नहीं होती है) और मूल राज्य में सभी को बहाल कर देगा।
बेशक, कॉल returrnटारगेट या उसके तर्कों के माध्यम से फोन करने वाले को किसी भी तरह से स्पष्ट रूप से फ़ंक्शन से बाहर निकलने से रोकना नहीं है। ऐसा करने से किसी भी राज्य की बहाली / सफाई को रोक दिया जाएगा अन्यथा यह प्रयास करेगा।
यह सब करने के लिए तीन evalगहरी जाने की जरूरत है । पहले यह खुद को एक स्थानीय दायरे में लपेटता है, फिर, भीतर से, यह तर्कों में पढ़ता है, उन्हें मान्य शेल नामों के लिए मान्य करता है, और त्रुटि के साथ छोड़ता है अगर यह नहीं मिलता है। यदि सभी तर्क वैध हैं और अंततः एक कारण command -v "$1"सच हो जाता है (याद रखें: $PATHइस बिंदु पर खाली है) तो यह evalकमांड-लाइन को परिभाषित करेगा और रैप टारगेट के सभी शेष तर्कों को पारित करेगा (हालांकि इसके लिए विशेष मामले की अनदेखी करता है ns- क्योंकि वह होगा नहीं 'बहुत उपयोगी है, और तीन evalएस गहरी पर्याप्त से अधिक है) ।
यह मूल रूप से इस तरह काम करता है:
case $- in (*c*) ... # because set -c doesnt work
esac
_PATH=$PATH PATH= OPTS=$- some=vars \
command eval LOCALS=${list_of_LOCALS}'
for a do i=$((i+1)) # arg ref
if [ "$a" != ns ] && # ns ns would be silly
command -v "$a" &&
! alias "$a" # aliases are hard to run quoted
then eval " PATH=\$_PATH OTHERS=$DEFAULTS $v \
command eval '\''
shift $((i-1)) # leave only tgt in @
case $OPTS in (*different*)
set \"-\${OPTS}\" # init shell opts
esac
\"\$@\" # run simple command
set +$- -$OPTS "$?" # save return, restore opts
'\''"
cd -P /dev/fd/7/ # go whence we came
return "$(($??$?:$1))" # return >0 for cd else $1
else case $a in (*badname*) : get mad;;
# rest of arg sa${v}es
esac
fi; done
' 7<.
कुछ अन्य पुनर्निर्देशन और, और कुछ अजीब परीक्षण हैं जिस तरह से कुछ गोले डालते cहैं $-और फिर इसे set (???) के विकल्प के रूप में स्वीकार करने से इनकार करते हैं , लेकिन इसका सभी सहायक और मुख्य रूप से सिर्फ उत्सर्जन से बचाने के लिए उपयोग किया जाता है। अवांछित उत्पादन और बढ़त के मामलों में समान है। और इसी तरह यह काम करता है। यह उन चीजों को कर सकता है क्योंकि यह अपने लपेटे हुए उपयोगिता को एक नेस्टेड में कॉल करने से पहले अपना स्थानीय दायरा सेट करता है।
यह लंबा है, क्योंकि मैं यहां बहुत सावधान रहने की कोशिश करता हूं - तीन evalsकठिन है। लेकिन इसके साथ आप यह कर सकते हैं:
ns X=local . /dev/fd/0 <<""; echo "$X" "$Y"
X=still_local
Y=global
echo "$X" "$Y"
still_local global
global
लिपटे उपयोगिता के स्थानीय स्कोप को लगातार आगे बढ़ाते हुए नाम देना बहुत कठिन नहीं होना चाहिए। और यहां तक कि जैसा कि लिखा गया है, यह पहले $LOCALSसे ही लिपटे उपयोगिता के लिए एक चर को परिभाषित करता है जो सभी नामों की केवल एक अलग-अलग सूची से मिलकर बनता है जो इसे लिपटे उपयोगिता के वातावरण में परिभाषित करता है।
पसंद:
ns var1=something var2= eval ' printf "%-10s%-10s%-10s%s\n" $LOCALS '
... जो पूरी तरह से सुरक्षित है - $IFSइसे अपने डिफ़ॉल्ट मूल्य के लिए पवित्रा कर दिया गया है और केवल वैध शेल नाम इसे बनाते हैं $LOCALSजब तक कि आप इसे स्वयं कमांड लाइन पर सेट न करें। और यहां तक कि अगर एक विभाजित चर में ग्लोब वर्ण हो सकते हैं, तो आप OPTS=fअपने विस्तार को प्रतिबंधित करने के लिए लिपटे उपयोगिता के लिए कमांड-लाइन पर भी सेट कर सकते हैं । किसी भी स्थिति में:
LOCALS ARG0 ARGC HOME
IFS OLDPWD OPTARG OPTIND
OPTS PATH PS3 PS4
PWD a b c
d e f g
h i j k
l m n o
p q r s
t u v w
x y z _
bel bs cr esc
ht ff lf vt
lb dq ds rb
sq var1 var2
और यहाँ फ़ंक्शन है। विस्तार \से बचने के लिए सभी कमांड्स पूर्वनिर्मित w / हैं alias:
ns(){ ${1+":"} return
case $- in
(c|"") ! set "OPTS=" "$@"
;; (*c*) ! set "OPTS=${-%c*}${-#*c}" "$@"
;; (*) set "OPTS=$-" "$@"
;; esac
OPTS=${1#*=} _PATH=$PATH PATH= LOCALS= lf='
' rb=\} sq=\' l= a= i=0 v= __=$_ IFS=" ""
" command eval LOCALS=\"LOCALS \
ARG0 ARGC HOME IFS OLDPWD OPTARG OPTIND OPTS \
PATH PS3 PS4 PWD a b c d e f g h i j k l m n \
o p q r s t u v w x y z _ bel bs cr esc ht ff \
lf vt lb dq ds rb sq'"
for a do i=$((i+1))
if \[ ns != "$a" ] &&
\command -v "$a" >&9 &&
! \alias "${a%%=*}" >&9 2>&9
then \eval 7>&- '\' \
'ARGC=$((-i+$#)) ARG0=$a HOME=~' \
'OLDPWD=$OLDPWD PATH=$_PATH IFS=$IFS' \
'OPTARG=$OPTARG PWD=$PWD OPTIND=1' \
'PS3=$PS3 _=$__ PS4=$PS4 LOCALS=$LOCALS' \
'a= b= c= d= e= f= g= i=0 j= k= l= m= n= o=' \
'p= q= r= s= t= u= v= w= x=0 y= z= ht=\ ' \
'cr=^M bs=^H ff=^L vt=^K esc=^[ bel=^G lf=$lf' \
'dq=\" sq=$sq ds=$ lb=\{ rb=\}' \''"$v' \
'\command eval 9>&2 2>&- '\' \
'\shift $((i-1));' \
'case \${OPTS##*[!A-Za-z]*} in' \
'(*[!c$OPTS]*) >&- 2>&9"'\' \
'\set -"${OPTS%c*}${OPTS#*c}"' \
';;esac; "$@" 2>&9 9>&-; PS4= ' \
'\set +"${-%c*}${-#*c}"'\'\" \
-'$OPTS \"\$?\"$sq";' \
' \cd -- "${OLDPWD:-$PWD}"
\cd -P ${ANDROID_SYSTEM+"/proc/self/fd/7"} /dev/fd/7/
\return "$(($??$?:$1))"
else case ${a%%=*} in
([0-9]*|""|*[!_[:alnum:]]*)
\printf "%s: \${$i}: Invalid name: %s\n" \
>&2 "$0: ns()" "'\''${a%%=*}'\''"
\return 2
;; ("$a") v="$v $a=\$$a"
;; (*) v="$v ${a%%=*}=\${$i#*=}"
;; esac
case " $LOCALS " in (*" ${a%%=*} "*)
;; (*) LOCALS=$LOCALS" ${a%%=*}"
;; esac
fi
done' 7<. 9<>/dev/null
}
( easiest thing ever )। लेकिन यह काफी नहीं है कि आप क्या कर रहे हैं। मुझे लगता है कि आप कर सकते हैं( stuff in subshell; exec env ) | sed 's/^/namespace_/'औरevalमूल शेल में परिणाम है, लेकिन यह बुरा है।