बैश फ़ंक्शन से एक स्ट्रिंग मान कैसे लौटाएं


461

मैं बैश फंक्शन से एक स्ट्रिंग लौटना चाहता हूं।

मैं जावा में उदाहरण लिखूंगा कि मैं क्या करना चाहता हूं:

public String getSomeString() {
  return "tadaa";
}

String variable = getSomeString();

नीचे दिया गया उदाहरण बैश में काम करता है, लेकिन क्या ऐसा करने का एक बेहतर तरीका है?

function getSomeString {
   echo "tadaa"
}

VARIABLE=$(getSomeString)

4
एक तरफ के रूप में, function funcName {पूर्व- POSIX विरासत सिंटैक्स है जो प्रारंभिक ksh से विरासत में मिला है (जहां इसके शब्दार्थ अंतर थे जो सम्मान प्रदान करते हैं)। funcName() {, के साथ function, इसके बजाय का उपयोग किया जाना चाहिए; देख wiki.bash-hackers.org/scripting/obsolete
चार्ल्स डफी

वह लिंक NAME () COMPOUND-CMD या फ़ंक्शन NAME {CMDS का उपयोग करने के लिए कहता है; } तो function myFunction { blah; }ठीक है; यह function myFunction() { blah }ठीक नहीं है, यानी कीवर्ड फ़ंक्शन के साथ कोष्ठक का उपयोग।
विल

जवाबों:


290

मुझे इससे बेहतर कोई तरीका नहीं है। बैश केवल स्टेटस कोड (पूर्णांक) और स्ट्रिंग को लिखे गए तार जानता है।


15
+1 @ tomas-f: आपको वास्तव में इस बात पर सावधान रहना होगा कि आपके पास इस फ़ंक्शन में "getSomeString ()" के रूप में कोई भी कोड है जो अंततः प्रतिध्वनित होगा, जिसका अर्थ है कि आपको गलत रिटर्न स्ट्रिंग मिलेगा।
मणि

11
यह सीधे तौर पर गलत है। आप रिटर्न वैरिएबल के अंदर मनमाना डेटा वापस कर सकते हैं। जो स्पष्ट रूप से एक बेहतर तरीका है।
Evi1M4chine

36
@ Evi1M4chine, उम ... नहीं, आप नहीं कर सकते। आप एक वैश्विक चर सेट कर सकते हैं और इसे "रिटर्न" कह सकते हैं, जैसा कि मैं देख रहा हूं कि आप अपनी स्क्रिप्ट में करते हैं। लेकिन फिर वह सम्मेलन द्वारा, वास्तव में आपके कोड के निष्पादन के लिए प्रोग्रामिक रूप से बंधा हुआ नहीं है। "स्पष्ट रूप से एक बेहतर तरीका"? उम नहीं। कमांड प्रतिस्थापन अधिक स्पष्ट और मॉड्यूलर है।
वाइल्डकार्ड

6
"कमांड प्रतिस्थापन बहुत अधिक स्पष्ट और मॉड्यूलर है" प्रासंगिक होगा यदि प्रश्न कमांड के बारे में था; यह सवाल है कि कैसे एक बैश फ़ंक्शन से एक स्ट्रिंग लौटाया जाए! बैश 4.3 (2014?) के बाद से ओपी ने जो पूछा है, उसे करने के लिए एक तरह से बनाया गया है - नीचे मेरा जवाब देखें।
3

4
मूल प्रश्न में इसे करने का सबसे सरल तरीका है, और ज्यादातर मामलों में अच्छी तरह से काम करता है। बैश रिटर्न वैल्यू को शायद "रिटर्न कोड" कहा जाना चाहिए क्योंकि वे स्क्रिप्टिंग में मानक रिटर्न मानों की तरह कम हैं, और संख्यात्मक शेल कमांड एक्जिट कोड्स की तरह अधिक हैं (आप सामान की तरह कर सकते हैं somefunction && echo 'success')। यदि आप किसी अन्य कमांड की तरह फंक्शन के बारे में सोचते हैं, तो यह समझ में आता है; आदेश एक स्थिति कोड के अलावा बाहर निकलने पर कुछ भी "वापस" नहीं करते हैं, लेकिन वे इस बीच चीजों को आउटपुट कर सकते हैं जो आप कैप्चर कर सकते हैं।
बीजोर

193

आप फ़ंक्शन को पहले arg के रूप में एक चर ले सकते हैं और उस चर के साथ चर को संशोधित कर सकते हैं जिसे आप वापस करना चाहते हैं।

#!/bin/bash
set -x
function pass_back_a_string() {
    eval "$1='foo bar rab oof'"
}

return_var=''
pass_back_a_string return_var
echo $return_var

प्रिंट्स "फू बार रब ऊफ"।

संपादित करें : @Luca Borrione की टिप्पणी को संबोधित करने के लिए स्ट्रिंग में व्हाट्सएप की अनुमति देने के लिए उपयुक्त स्थान पर उद्धृत किया गया।

संपादित करें : प्रदर्शन के रूप में, निम्नलिखित कार्यक्रम देखें। यह एक सामान्य-उद्देश्य समाधान है: यह आपको स्थानीय चर में एक स्ट्रिंग प्राप्त करने की अनुमति देता है।

#!/bin/bash
set -x
function pass_back_a_string() {
    eval "$1='foo bar rab oof'"
}

return_var=''
pass_back_a_string return_var
echo $return_var

function call_a_string_func() {
     local lvar=''
     pass_back_a_string lvar
     echo "lvar='$lvar' locally"
}

call_a_string_func
echo "lvar='$lvar' globally"

यह प्रिंट:

+ return_var=
+ pass_back_a_string return_var
+ eval 'return_var='\''foo bar rab oof'\'''
++ return_var='foo bar rab oof'
+ echo foo bar rab oof
foo bar rab oof
+ call_a_string_func
+ local lvar=
+ pass_back_a_string lvar
+ eval 'lvar='\''foo bar rab oof'\'''
++ lvar='foo bar rab oof'
+ echo 'lvar='\''foo bar rab oof'\'' locally'
lvar='foo bar rab oof' locally
+ echo 'lvar='\'''\'' globally'
lvar='' globally

संपादित करें : प्रदर्शन है कि मूल वेरिएबल का मान है समारोह में उपलब्ध है, के रूप में गलत तरीके से एक टिप्पणी में @Xichen ली ने आलोचना की थी।

#!/bin/bash
set -x
function pass_back_a_string() {
    eval "echo in pass_back_a_string, original $1 is \$$1"
    eval "$1='foo bar rab oof'"
}

return_var='original return_var'
pass_back_a_string return_var
echo $return_var

function call_a_string_func() {
     local lvar='original lvar'
     pass_back_a_string lvar
     echo "lvar='$lvar' locally"
}

call_a_string_func
echo "lvar='$lvar' globally"

यह आउटपुट देता है:

+ return_var='original return_var'
+ pass_back_a_string return_var
+ eval 'echo in pass_back_a_string, original return_var is $return_var'
++ echo in pass_back_a_string, original return_var is original return_var
in pass_back_a_string, original return_var is original return_var
+ eval 'return_var='\''foo bar rab oof'\'''
++ return_var='foo bar rab oof'
+ echo foo bar rab oof
foo bar rab oof
+ call_a_string_func
+ local 'lvar=original lvar'
+ pass_back_a_string lvar
+ eval 'echo in pass_back_a_string, original lvar is $lvar'
++ echo in pass_back_a_string, original lvar is original lvar
in pass_back_a_string, original lvar is original lvar
+ eval 'lvar='\''foo bar rab oof'\'''
++ lvar='foo bar rab oof'
+ echo 'lvar='\''foo bar rab oof'\'' locally'
lvar='foo bar rab oof' locally
+ echo 'lvar='\'''\'' globally'
lvar='' globally

1
यह जवाब बहुत अच्छा है! पैरामीटर को C ++ में विचार के समान संदर्भों द्वारा पारित किया जा सकता है।
यूं हुआंग

4
उस उत्तर के बारे में किसी विशेषज्ञ से प्रतिक्रिया प्राप्त करना अच्छा होगा। मैंने ऐसा कभी नहीं देखा है कि लिपियों में इसका इस्तेमाल होता है, शायद एक अच्छे कारण के लिए। वैसे भी: यह +1 है इसे सही उत्तर के लिए मतदान किया जाना चाहिए था
जॉन

क्या यह fgmएक सरलीकृत तरीके से लिखे गए उत्तर के समान नहीं है ? यदि स्ट्रिंग fooमें सफेद स्थान हैं, तो यह काम नहीं करेगा , जबकि fgmवह दिखा रहा है।
लुका बोर्रिएन

4
@ XichenLi: अपने डाउनवोट के साथ एक टिप्पणी छोड़ने के लिए धन्यवाद; कृपया मेरा संपादन देखें। आप फ़ंक्शन के साथ चर का प्रारंभिक मान प्राप्त कर सकते हैं \$$1। यदि आप कुछ अलग खोज रहे हैं, तो कृपया मुझे बताएं।
bstpierre

1
@timiscoding जो एक के साथ तय किया जा सकता है printf '%q' "$var"। शेल से बचने के लिए% q एक प्रारूप स्ट्रिंग है। फिर इसे कच्चा ही पास करें।
bb010g

99

ऊपर दिए गए सभी उत्तर इस बात को नजरअंदाज करते हैं कि बैश के पेज में क्या बताया गया है।

  • किसी फ़ंक्शन के अंदर घोषित सभी चर कॉलिंग वातावरण के साथ साझा किए जाएंगे।
  • स्थानीय घोषित सभी चर साझा नहीं किए जाएंगे।

उदाहरण कोड

#!/bin/bash

f()
{
    echo function starts
    local WillNotExists="It still does!"
    DoesNotExists="It still does!"
    echo function ends
}

echo $DoesNotExists #Should print empty line
echo $WillNotExists #Should print empty line
f                   #Call the function
echo $DoesNotExists #Should print It still does!
echo $WillNotExists #Should print empty line

और आउटपुट

$ sh -x ./x.sh
+ echo

+ echo

+ f
+ echo function starts 
function starts
+ local 'WillNotExists=It still does!'
+ DoesNotExists='It still does!'
+ echo function ends 
function ends
+ echo It still 'does!' 
It still does!
+ echo

Pdksh और ksh के तहत भी यह स्क्रिप्ट वही करती है!


10
इस उत्तर की अपनी खूबियाँ हैं। मैं यह सोचकर यहां आया था कि मैं एक समारोह से एक स्ट्रिंग वापस करना चाहता था। इस जवाब ने मुझे एहसास दिलाया कि सिर्फ मेरी सी # -बेटी बात कर रही थी। मुझे संदेह है कि दूसरों को भी ऐसा ही अनुभव हो सकता है।
LOAS

4
@ElmarZander तुम गलत हो, यह पूरी तरह से प्रासंगिक है। यह वैश्विक दायरे को फ़ंक्शन-स्कोप मान में प्राप्त करने का एक सरल तरीका है, और कुछ इस बेहतर / सरल पर विचार करेंगे कि ग्लोबल वेरिएबल को bstpierre द्वारा उल्लिखित के रूप में फिर से परिभाषित करें।
कोमोडावेव

लोकल नॉन-बैश स्क्रिप्ट के लिए पोर्टेबल नहीं है, जो एक कारण है कि कुछ लोग इससे बचते हैं।
उज्ज्वल

प्रश्न: छोरों में चर के बारे में क्या?
अनु

1
एक मैक ($ bash --version GNU बैश, संस्करण 3.2.57 (1) -release (x86_64-apple-darwin14) कॉपीराइट (C) 2007 फ्री सॉफ्टवेयर फाउंडेशन, इंक।), यह सही है कि एक मेल खाने वाला डिजिटल वेरिएंट है इनिशियलाइज़ किया गया है, लेकिन जब मैं दूसरे फंक्शन f2 में उसी वेरिएबल को साइड-इफ़ेक्ट करने की कोशिश करता हूँ, तो वह साइड-इफ़ेक्ट कायम नहीं रहता है। तो, यह बहुत असंगत लगता है और इस प्रकार मेरे उपयोग के लिए अच्छा नहीं है।
ऐनीअजील

45

बैश, संस्करण ४.३ के बाद से, फेब २०१४ (?), में एक ही लाभकारी प्रदर्शन और अप्रत्यक्ष प्रभाव के साथ, "eval" से परे, संदर्भ चर या नाम संदर्भ (नामरेफ़्स) के लिए स्पष्ट समर्थन है, और जो आपकी लिपियों में स्पष्ट भी हो सकता है और कठिन भी। "भूल जाना" और इस त्रुटि को ठीक करना है:

declare [-aAfFgilnrtux] [-p] [name[=value] ...]
typeset [-aAfFgilnrtux] [-p] [name[=value] ...]
  Declare variables and/or give them attributes
  ...
  -n Give each name the nameref attribute, making it a name reference
     to another variable.  That other variable is defined by the value
     of name.  All references and assignments to name, except for
     changing the -n attribute itself, are performed on the variable
     referenced by name's value.  The -n attribute cannot be applied to
     array variables.
...
When used in a function, declare and typeset make each name local,
as with the local command, unless the -g option is supplied...

और भी:

पैरामीटर

एक वेरिएबल को nameref एट्रीब्यूट दिया जा सकता है, जिसमें ऐलान या लोकल बिल्टिन कमांड्स (एन डिक्लेयर और लोकल नीचे के डिस्क्रिप्शन देखें) के विकल्प का इस्तेमाल करके नेमरेफ या दूसरे वेरिएबल के लिए रेफरेंस बनाया जा सकता है। यह चर को अप्रत्यक्ष रूप से हेरफेर करने की अनुमति देता है। जब भी nameref वैरिएबल को संदर्भित या सौंपा जाता है, ऑपरेशन वास्तव में nameref वैरिएबल के मान द्वारा निर्दिष्ट चर पर किया जाता है। एक प्रकार का नाम आम तौर पर शेल फ़ंक्शंस के भीतर प्रयोग किया जाता है जिसका उल्लेख एक चर जिसका नाम एक तर्क के रूप में किया जाता है, को संदर्भित करता है। उदाहरण के लिए, यदि चर नाम को इसके पहले तर्क के रूप में शेल फ़ंक्शन में पास किया जाता है, तो चल रहा है

      declare -n ref=$1

फ़ंक्शन के अंदर एक nameref वैरिएबल रेफरी बनाता है जिसका मान पहले वाले तर्क के रूप में दिया गया वेरिएबल नेम है। रेफरी के लिए संदर्भ और असाइनमेंट को उस चर के संदर्भ और असाइनमेंट के रूप में माना जाता है जिसका नाम $ 1 था। यदि लूप के लिए नियंत्रण चर में नामांक गुण होता है, तो शब्दों की सूची शेल चरों की सूची हो सकती है, और सूची में प्रत्येक शब्द के लिए एक नाम संदर्भ स्थापित किया जाएगा, बदले में, जब लूप निष्पादित किया जाता है। सरणी चर को -n विशेषता नहीं दी जा सकती। हालाँकि, nameref वेरिएबल्स ऐरे वेरिएबल्स और सबस्क्रिप्टेड ऐरे वेरिएबल्स को रेफर कर सकते हैं। Namerefs को unset में निर्मित विकल्प का उपयोग करके परेशान किया जा सकता है। अन्यथा, अगर किसी तर्क के रूप में नामरे चर के नाम के साथ अनसेट को निष्पादित किया जाता है,

उदाहरण के लिए ( EDIT 2 : (शुक्रिया रॉन) नामांकित (पूर्व-उपकृत) फ़ंक्शन-आंतरिक चर नाम, बाहरी चर संघर्ष को कम करने के लिए, जो अंत में ठीक से जवाब देना चाहिए, करस्टन द्वारा टिप्पणियों में उठाए गए मुद्दे):

# $1 : string; your variable to contain the return value
function return_a_string () {
    declare -n ret=$1
    local MYLIB_return_a_string_message="The date is "
    MYLIB_return_a_string_message+=$(date)
    ret=$MYLIB_return_a_string_message
}

और इस उदाहरण का परीक्षण:

$ return_a_string result; echo $result
The date is 20160817

ध्यान दें कि एक फ़ंक्शन में उपयोग किए जाने पर बैश "घोषित" बिलिन, डिफ़ॉल्ट रूप से घोषित चर "स्थानीय" बनाता है, और "-n" का उपयोग "स्थानीय" के साथ भी किया जा सकता है।

मैं "उबाऊ स्थानीय" चर से "महत्वपूर्ण घोषणा" चर को अलग करना पसंद करता हूं, इसलिए "घोषित" और "स्थानीय" का उपयोग इस तरह से प्रलेखन के रूप में होता है।

EDIT 1 - (Karsten द्वारा नीचे टिप्पणी करने के लिए प्रतिक्रिया) - मैं किसी भी अधिक नीचे टिप्पणी नहीं जोड़ सकता, लेकिन Karsten की टिप्पणी मुझे मिल गई, इसलिए मैंने निम्नलिखित परीक्षण किया जो ठीक है, AFAICT - Karsten यदि आप इसे पढ़ते हैं, तो कृपया एक सटीक सेट प्रदान करें कमांड लाइन से परीक्षण चरणों में, जो समस्या आप मानते हैं, वह मौजूद है, क्योंकि ये निम्नलिखित चरण ठीक काम करते हैं:

$ return_a_string ret; echo $ret
The date is 20170104

(मैंने इसे अभी-अभी चलाया, उपरोक्त फ़ंक्शन को बैश टर्म में चिपकाने के बाद - जैसा कि आप देख सकते हैं, परिणाम ठीक काम करता है।)


4
यह मेरी आशा है कि यह शीर्ष पर पहुंच जाता है। eval एक अंतिम उपाय होना चाहिए। उल्लेख के योग्य है कि nameref चर केवल bash 4.3 ( चैंज के अनुसार ) (feb 2014 [] में जारी) के बाद से उपलब्ध हैं । यह महत्वपूर्ण है अगर पोर्टेबिलिटी एक चिंता का विषय है। कृपया इस तथ्य पर बैश मैनुअल का हवाला देंdeclare कार्यों के अंदर स्थानीय चर बनाता है (यह जानकारी इसके द्वारा नहीं दी गई है help declare): "... जब एक फ़ंक्शन में उपयोग किया जाता है, तो घोषणा और टाइपसेट प्रत्येक नाम को स्थानीय आदेश के अनुसार स्थानीय बनाते हैं, जब तक कि - g विकल्प की आपूर्ति की जाती है ... "
init_js

2
यह एक ही अलियासिंग समस्या है जिसका स्पष्ट समाधान है। जब आप किसी फ़ंक्शन को कॉल करते हैं और आउटपुट चर के नाम से गुजरते हैं, तो आपको उस चर के नाम को पारित करने से बचना होगा जो आपके द्वारा कॉल किए जाने वाले फ़ंक्शन के भीतर स्थानीय रूप से उपयोग किया जाता है। एन्कैप्सुलेशन के संदर्भ में यह एक बड़ी समस्या है, क्योंकि आप किसी फ़ंक्शन में नए स्थानीय वेरिएबल्स को जोड़ या उनका नाम नहीं बदल सकते हैं, यदि कोई भी फ़ंक्शन कॉलर आउटपुट पैरामीटर के लिए उस नाम का उपयोग करना चाहता है।
कर्स्टन

1
@ कर्स्टन सहमत हुए। दोनों मामलों (eval और namerefs) में, आपको एक अलग नाम चुनना होगा। Eval पर nameref दृष्टिकोण के साथ एक फायदा यह है कि किसी को भागने के तार से नहीं जूझना पड़ता। बेशक, आप हमेशा कुछ कर सकते हैं K=$1; V=$2; eval "$A='$V'";, लेकिन एक गलती (जैसे एक खाली या छोड़ा हुआ पैरामीटर), और यह अधिक खतरनाक होगा। यदि आप "संदेश" को "रिटायर" के बजाय रिटर्न वैरिएबल नाम के रूप में चुनते हैं तो @zenaan @Karsten द्वारा उठाया गया मुद्दा लागू होता है।
init_js

3
संभवतः एक फ़ंक्शन को एक नेमेरेफ़ तर्क को स्वीकार करने के लिए शुरुआत से डिज़ाइन किया जाना चाहिए, इसलिए फ़ंक्शन लेखक को नाम टकराव की संभावना के बारे में पता होना चाहिए और इससे बचने के लिए कुछ विशिष्ट सम्मेलन का उपयोग कर सकते हैं। उदाहरण के लिए, फ़ंक्शन X के अंदर, कन्वेंशन के साथ स्थानीय चर नाम "X_LOCAL_name"।
रॉन बुर्क

34

ऊपर दिए गए bstpierre की तरह , मैं स्पष्ट रूप से नामकरण आउटपुट चर का उपयोग करने की सलाह देता हूं:

function some_func() # OUTVAR ARG1
{
   local _outvar=$1
   local _result # Use some naming convention to avoid OUTVARs to clash
   ... some processing ....
   eval $_outvar=\$_result # Instead of just =$_result
}

$ उद्धृत करने के उपयोग पर ध्यान दें। यह सामग्री $resultको शेल विशेष वर्णों में व्याख्या करने से बचाएगा । मैंने पाया है कि यह एक प्रतिध्वनि को पकड़ने के मुहावरे से कहीं अधिक परिमाण का क्रम हैresult=$(some_func "arg1") । MSYS पर बैश का उपयोग करने से गति अंतर और भी अधिक उल्लेखनीय लगता है, जहां फ़ंक्शन कॉल से कैप्चरिंग लगभग विनाशकारी है।

स्थानीय चर में भेजना ठीक है क्योंकि स्थानीय लोग गतिशील रूप से बैश में हैं:

function another_func() # ARG
{
   local result
   some_func result "$1"
   echo result is $result
}

4
यह मेरी मदद करता है क्योंकि मैं डिबगिंग / लॉगिंग उद्देश्यों के लिए कई इको स्टेटमेंट्स का उपयोग करना पसंद करता हूं। गूंज को पकड़ने का मुहावरा विफल हो जाता है क्योंकि यह उन सभी को पकड़ लेता है। धन्यवाद!
ऐनीएजाइल

यह (दूसरा सबसे अच्छा) उचित समाधान है! स्वच्छ, तेज, सुरुचिपूर्ण, समझदार।
Evi1M4chine

वास्तविक रखने के लिए +2। मैं कहने वाला था। echoकमांड प्रतिस्थापन के साथ संयुक्त रूप से एक फ़ंक्शन के अंदर संयोजन को इतने सारे लोग कैसे अनदेखा कर सकते हैं !
एंथनी रटलेज

22

आप फ़ंक्शन आउटपुट पर भी कब्जा कर सकते हैं:

#!/bin/bash
function getSomeString() {
     echo "tadaa!"
}

return_var=$(getSomeString)
echo $return_var
# Alternative syntax:
return_var=`getSomeString`
echo $return_var

अजीब लगता है, लेकिन वैश्विक चर IMHO का उपयोग करने से बेहतर है। पासिंग पैरामीटर हमेशा की तरह काम करता है, बस उन्हें ब्रेसिज़ या बैकटिक्स के अंदर रखें।


11
वैकल्पिक वाक्यविन्यास नोट के अलावा, क्या यह वही नहीं है जो पहले से ही अपने प्रश्न में लिखा है?
लुका बोर्रिएन

12

सबसे सीधा और मजबूत समाधान कमांड प्रतिस्थापन का उपयोग करना है, जैसा कि अन्य लोगों ने लिखा है:

assign()
{
    local x
    x="Test"
    echo "$x"
}

x=$(assign) # This assigns string "Test" to x

नकारात्मक पक्ष प्रदर्शन है क्योंकि इसके लिए एक अलग प्रक्रिया की आवश्यकता होती है।

इस विषय में सुझाई गई अन्य तकनीक, एक तर्क के रूप में निर्दिष्ट करने के लिए एक चर के नाम से गुजरती है, इसके दुष्प्रभाव हैं, और मैं इसे इसके मूल रूप में अनुशंसित नहीं करूंगा। समस्या यह है कि आपको रिटर्न वैल्यू की गणना करने के लिए फ़ंक्शन में कुछ चर की आवश्यकता होगी, और ऐसा हो सकता है कि रिटर्न वैल्यू को स्टोर करने के उद्देश्य से वेरिएबल का नाम उनमें से किसी एक के साथ हस्तक्षेप करेगा:

assign()
{
    local x
    x="Test"
    eval "$1=\$x"
}

assign y # This assigns string "Test" to y, as expected

assign x # This will NOT assign anything to x in this scope
         # because the name "x" is declared as local inside the function

आप निश्चित रूप से, फ़ंक्शन के आंतरिक चर को स्थानीय घोषित नहीं कर सकते हैं, लेकिन आपको वास्तव में हमेशा ऐसा करना चाहिए अन्यथा आप कर सकते हैं, दूसरी तरफ, गलती से माता-पिता के दायरे से एक असंबंधित चर को अधिलेखित कर सकते हैं यदि एक ही नाम है। ।

एक संभावित समाधान वैश्विक रूप से पारित चर की एक स्पष्ट घोषणा है:

assign()
{
    local x
    eval declare -g $1
    x="Test"
    eval "$1=\$x"
}

यदि नाम "x" को एक तर्क के रूप में पारित किया जाता है, तो फ़ंक्शन निकाय की दूसरी पंक्ति पिछले स्थानीय घोषणा को अधिलेखित कर देगी। लेकिन नाम खुद को अभी भी बाधित कर सकते हैं, इसलिए यदि आप वापसी मूल्य लिखने से पहले पारित चर में पहले से संग्रहीत मूल्य का उपयोग करने का इरादा रखते हैं, तो ध्यान रखें कि आपको इसे शुरुआत में किसी अन्य स्थानीय चर में कॉपी करना होगा; अन्यथा परिणाम अप्रत्याशित होगा! इसके अलावा, यह केवल BASH के सबसे हाल के संस्करण में काम करेगा, अर्थात् 4.2। अधिक पोर्टेबल कोड एक ही प्रभाव के साथ स्पष्ट सशर्त निर्माण का उपयोग कर सकते हैं:

assign()
{
    if [[ $1 != x ]]; then
      local x
    fi
    x="Test"
    eval "$1=\$x"
}

शायद सबसे सुरुचिपूर्ण समाधान केवल फ़ंक्शन रिटर्न मानों के लिए एक वैश्विक नाम आरक्षित करना है और इसे आपके द्वारा लिखे गए प्रत्येक फ़ंक्शन में लगातार उपयोग करना है।


3
यह ^ ^ ^ ^। अनजाने में होने वाला अलियासिंग, इनकैप्सुलेशन को तोड़ता है, दोनों evalऔर declare -nसमाधानों के साथ बड़ी समस्या है । resultसभी आउटपुट मापदंडों के लिए एक एकल समर्पित चर नाम होने का समाधान एकमात्र ऐसा समाधान लगता है जिसके लिए संघर्षों से बचने के लिए सभी कॉलर्स को जानने के लिए फ़ंक्शन की आवश्यकता नहीं होती है।
कर्स्टन

12

जैसा कि पहले उल्लेख किया गया है, फ़ंक्शन से स्ट्रिंग वापस करने का "सही" तरीका कमांड प्रतिस्थापन के साथ है। इस घटना में कि फ़ंक्शन को कंसोल को आउटपुट करने की आवश्यकता है (जैसा कि @Mani ऊपर उल्लेख किया गया है), फ़ंक्शन की शुरुआत में एक अस्थायी fd बनाएं और कंसोल को रीडायरेक्ट करें। अपनी स्ट्रिंग वापस करने से पहले अस्थायी fd को बंद करें।

#!/bin/bash
# file:  func_return_test.sh
returnString() {
    exec 3>&1 >/dev/tty
    local s=$1
    s=${s:="some default string"}
    echo "writing directly to console"
    exec 3>&-     
    echo "$s"
}

my_string=$(returnString "$*")
echo "my_string:  [$my_string]"

स्क्रिप्ट को बिना किसी पैरेम्स के निष्पादित करना ...

# ./func_return_test.sh
writing directly to console
my_string:  [some default string]

आशा है कि यह लोगों की मदद करता है

-Andy


6
इसके उपयोग हैं, लेकिन पूरे पर आपको कंसोल पर एक स्पष्ट पुनर्निर्देशन करने से बचना चाहिए; आउटपुट पहले से ही पुनर्निर्देशित हो सकता है, या स्क्रिप्ट एक ऐसे संदर्भ में चल रही हो सकती है जहां कोई भी मौजूद नहीं है। आप 3>&1स्क्रिप्ट के शीर्ष पर नकल करके &1 &3और फिर &4फ़ंक्शन के भीतर किसी अन्य प्लेसहोल्डर को नकल करके प्राप्त कर सकते हैं । बदसूरत सभी दौर, हालांकि।
जेएमबी

8

आप एक वैश्विक चर का उपयोग कर सकते हैं:

declare globalvar='some string'

string ()
{
  eval  "$1='some other string'"
} # ----------  end of function string  ----------

string globalvar

echo "'${globalvar}'"

यह देता है

'some other string'

6

एंडी के उत्तर पर मेरी टिप्पणी को स्पष्ट करने के लिए, अतिरिक्त फ़ाइल डिस्क्रिप्टर हेरफेर के उपयोग से बचने के लिए /dev/tty:

#!/bin/bash

exec 3>&1

returnString() {
    exec 4>&1 >&3
    local s=$1
    s=${s:="some default string"}
    echo "writing to stdout"
    echo "writing to stderr" >&2
    exec >&4-
    echo "$s"
}

my_string=$(returnString "$*")
echo "my_string:  [$my_string]"

अभी भी बुरा, हालांकि।


3

जिस तरह से आपके पास है वह ऐसा करने का एकमात्र तरीका है जो बिना दायरे को तोड़े। बैश में रिटर्न प्रकार की अवधारणा नहीं है, बस कोड और फ़ाइल डिस्क्रिप्टर से बाहर निकलें (स्टडिन / आउट / इरेट, आदि)


3

निम्नलिखित कोड पर विचार करते हुए, विक्की रोनेन के सिर को ऊपर उठाते हुए :

function use_global
{
    eval "$1='changed using a global var'"
}

function capture_output
{
    echo "always changed"
}

function test_inside_a_func
{
    local _myvar='local starting value'
    echo "3. $_myvar"

    use_global '_myvar'
    echo "4. $_myvar"

    _myvar=$( capture_output )
    echo "5. $_myvar"
}

function only_difference
{
    local _myvar='local starting value'
    echo "7. $_myvar"

    local use_global '_myvar'
    echo "8. $_myvar"

    local _myvar=$( capture_output )
    echo "9. $_myvar"
}

declare myvar='global starting value'
echo "0. $myvar"

use_global 'myvar'
echo "1. $myvar"

myvar=$( capture_output )
echo "2. $myvar"

test_inside_a_func
echo "6. $_myvar" # this was local inside the above function

only_difference



दे देंगे

0. global starting value
1. changed using a global var
2. always changed
3. local starting value
4. changed using a global var
5. always changed
6. 
7. local starting value
8. local starting value
9. always changed

हो सकता है कि सामान्य परिदृश्य test_inside_a_funcफ़ंक्शन में उपयोग किए गए सिंटैक्स का उपयोग करना है, इस प्रकार आप अधिकांश मामलों में दोनों तरीकों का उपयोग कर सकते हैं, हालांकि आउटपुट कैप्चर करना किसी भी स्थिति में हमेशा सुरक्षित कार्य है, एक फ़ंक्शन से वापसी मान की नकल करना जो आप कर सकते हैं अन्य भाषाओं में खोजें, जैसा कि Vicky Ronnenसही ढंग से बताया गया है।


2

मुझे लगता है कि सभी विकल्पों की गणना की गई है। किसी एक को चुनना आपके विशेष एप्लिकेशन के लिए सबसे अच्छी शैली की बात हो सकती है, और उस नस में, मैं एक विशेष शैली की पेशकश करना चाहता हूं जिसे मैंने उपयोगी पाया है। बैश में, चर और फ़ंक्शन समान नामस्थान में नहीं हैं। इसलिए, फ़ंक्शन के मान के रूप में उसी नाम के चर का इलाज करना एक कन्वेंशन है जो मुझे लगता है कि नाम क्लैश को कम करता है और पठनीयता को बढ़ाता है, अगर मैं इसे सख्ती से लागू करता हूं। वास्तविक जीवन से एक उदाहरण:

UnGetChar=
function GetChar() {
    # assume failure
    GetChar=
    # if someone previously "ungot" a char
    if ! [ -z "$UnGetChar" ]; then
        GetChar="$UnGetChar"
        UnGetChar=
        return 0               # success
    # else, if not at EOF
    elif IFS= read -N1 GetChar ; then
        return 0           # success
    else
        return 1           # EOF
    fi
}

function UnGetChar(){
    UnGetChar="$1"
}

और, इस तरह के कार्यों का उपयोग करने का एक उदाहरण:

function GetToken() {
    # assume failure
    GetToken=
    # if at end of file
    if ! GetChar; then
        return 1              # EOF
    # if start of comment
    elif [[ "$GetChar" == "#" ]]; then
        while [[ "$GetChar" != $'\n' ]]; do
            GetToken+="$GetChar"
            GetChar
        done
        UnGetChar "$GetChar"
    # if start of quoted string
    elif [ "$GetChar" == '"' ]; then
# ... et cetera

जैसा कि आप देख सकते हैं, जब आप की आवश्यकता होती है, तो उपयोग करने के लिए वापसी की स्थिति होती है या यदि आप नहीं करते हैं तो अनदेखा करें। "लौटाया गया" चर का उपयोग इसी तरह किया जा सकता है या अनदेखा किया जा सकता है, लेकिन इसके बाद ही से फ़ंक्शन के लागू के ।

बेशक, यह केवल एक सम्मेलन है। आप लौटने से पहले संबंधित मूल्य निर्धारित करने में विफल रहने के लिए स्वतंत्र हैं (इसलिए फ़ंक्शन के शुरू में इसे हमेशा शून्य करने के लिए मेरा सम्मेलन) या फिर फ़ंक्शन को कॉल करके (संभवतः अप्रत्यक्ष रूप से) इसके मूल्य को रौंदने के लिए। फिर भी, यह एक सम्मेलन है जो मुझे बहुत उपयोगी लगता है अगर मैं खुद को बैश कार्यों का भारी उपयोग करता हुआ पाता हूं।

इस भावना के विपरीत कि यह एक संकेत है, उदाहरण के लिए "पर्ल के लिए कदम" होना चाहिए, मेरा दर्शन यह है कि किसी भी भाषा की जटिलता के प्रबंधन के लिए कन्वेंशन हमेशा महत्वपूर्ण होते हैं।


2

आप echoएक स्ट्रिंग कर सकते हैं , लेकिन इसे पाइपिंग ( |) फ़ंक्शन को कुछ और से पकड़ सकते हैं ।

आप इसे कर सकते हैं expr, हालांकि शेलचेक ने इस उपयोग को पदावनत के रूप में रिपोर्ट किया है।


परेशानी यह है कि पाइप के दाईं ओर की वस्तु एक उपधारा है। तो myfunc | read OUTPUT ; echo $OUTPUTपैदावार कुछ भी नहीं। myfunc | ( read OUTPUT; echo $OUTPUT )अपेक्षित मूल्य प्राप्त करता है और स्पष्ट करता है कि दाईं ओर क्या हो रहा है। लेकिन निश्चित रूप से OUTPUT तब उपलब्ध नहीं है जहां आपको इसकी आवश्यकता हो ...
एड रैंडल

2

वे किसी भी 'नामित आउटपुट वैरिएबल' स्कीम की मुख्य समस्या हैं, जहां कॉल करने वाला वेरिएबल नाम (चाहे उपयोग कर रहा हो evalया नहीं declare -n) पास हो सकता है, अर्थात नाम क्लैश: एक एनकैप्सुलेशन बिंदु से, यह जोड़ने या नाम बदलने में सक्षम नहीं होने के लिए भयानक है। एक फ़ंक्शन में एक स्थानीय चर, सभी फ़ंक्शन के कॉलर्स की जांच किए बिना यह सुनिश्चित करने के लिए कि वे आउटपुट पैरामीटर के समान नाम पास नहीं करना चाहते हैं। (या दूसरी दिशा में, मैं नहीं चाहता कि जिस फ़ंक्शन का मैं उपयोग करने के लिए आउटपुट पैरामीटर का उपयोग करने का इरादा रखता हूं, उस फ़ंक्शन के स्रोत को पढ़ना आवश्यक है जो उस फ़ंक्शन में स्थानीय नहीं है।)

इसका एक ही तरीका है कि एक एकल समर्पित आउटपुट वैरिएबल का उपयोग REPLYकिया जाए (जैसे कि Evi1M4chine द्वारा सुझाया गया ) या एक कन्वेंशन जैसे रॉन बुर्क द्वारा सुझाया गया

हालाँकि, यह संभव है कि फ़ंक्शन आंतरिक रूप से एक निश्चित आउटपुट चर का उपयोग करें , और फिर कॉल करने वाले से इस तथ्य को छिपाने के लिए शीर्ष पर थोड़ी चीनी जोड़ें , जैसा कि मैंने callनिम्न उदाहरण में फ़ंक्शन के साथ किया है । इसे अवधारणा का प्रमाण मानें, लेकिन प्रमुख बिंदु हैं

  • फ़ंक्शन हमेशा रिटर्न मान देता है REPLY, और हमेशा की तरह एक निकास कोड भी लौटा सकता है
  • कॉलर के दृष्टिकोण से, वापसी मान किसी भी चर (स्थानीय या वैश्विक) सहित सौंपा जा सकता है REPLY( wrapperउदाहरण देखें )। फंक्शन का एग्जिट कोड पास हो जाता है, इसलिए उनका इस्तेमाल करते हैं जैसे कि a ifयाwhile या इसी तरह के निर्माणों का काम करता है के रूप में उम्मीद।
  • Syntactically फ़ंक्शन कॉल अभी भी एक एकल सरल कथन है।

इसका कारण यह है क्योंकि callफ़ंक्शन में स्वयं कोई लोकल नहीं है और REPLYनाम बंद होने की किसी भी संभावना से बचने के अलावा अन्य कोई चर का उपयोग नहीं करता है । उस बिंदु पर जहां कॉलर-परिभाषित आउटपुट चर नाम असाइन किया गया है, हम प्रभावी रूप से कॉलर के कार्यक्षेत्र में हैं (तकनीकी रूप से callफ़ंक्शन के समान दायरे में ), बजाय फ़ंक्शन के दायरे में कहा जा रहा है।

#!/bin/bash
function call() { # var=func [args ...]
  REPLY=; "${1#*=}" "${@:2}"; eval "${1%%=*}=\$REPLY; return $?"
}

function greet() {
  case "$1" in
    us) REPLY="hello";;
    nz) REPLY="kia ora";;
    *) return 123;;
  esac
}

function wrapper() {
  call REPLY=greet "$@"
}

function main() {
  local a b c d
  call a=greet us
  echo "a='$a' ($?)"
  call b=greet nz
  echo "b='$b' ($?)"
  call c=greet de
  echo "c='$c' ($?)"
  call d=wrapper us
  echo "d='$d' ($?)"
}
main

आउटपुट:

a='hello' (0)
b='kia ora' (0)
c='' (123)
d='hello' (0)

1

स्केलर और एरे वैल्यू ऑब्जेक्ट दोनों को वापस करने के लिए बैश पैटर्न :

परिभाषा

url_parse() { # parse 'url' into: 'url_host', 'url_port', ...
   local "$@" # inject caller 'url' argument in local scope
   local url_host="..." url_path="..." # calculate 'url_*' components
   declare -p ${!url_*} # return only 'url_*' object fields to the caller
}

मंगलाचरण

main() { # invoke url parser and inject 'url_*' results in local scope
   eval "$(url_parse url=http://host/path)" # parse 'url'
   echo "host=$url_host path=$url_path" # use 'url_*' components
}

0

मेरे कार्यक्रमों में, सम्मेलन द्वारा, यह वही है जो पहले से मौजूद $REPLYचर के लिए है, जो readउस सटीक उद्देश्य के लिए उपयोग करता है।

function getSomeString {
  REPLY="tadaa"
}

getSomeString
echo $REPLY

यह echoतों

tadaa

लेकिन संघर्षों से बचने के लिए, कोई अन्य वैश्विक चर करेगा।

declare result

function getSomeString {
  result="tadaa"
}

getSomeString
echo $result

यदि यह पर्याप्त नहीं है, तो मैं Markarian451 के समाधान की सलाह देता हूं ।


-2
agt@agtsoft:~/temp$ cat ./fc 
#!/bin/sh

fcall='function fcall { local res p=$1; shift; fname $*; eval "$p=$res"; }; fcall'

function f1 {
    res=$[($1+$2)*2];
}

function f2 {
    local a;
    eval ${fcall//fname/f1} a 2 3;
    echo f2:$a;
}

a=3;
f2;
echo after:a=$a, res=$res

agt@agtsoft:~/temp$ ./fc
f2:10
after:a=3, res=
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.