Bash işlevinden dize değeri döndürme


462

Bir Bash işlevinden bir dize döndürmek istiyorum.

Java'da ne yapmak istediğimi göstermek için örnek yazacağım:

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

String variable = getSomeString();

Aşağıdaki örnek bash'de çalışır, ancak bunu yapmanın daha iyi bir yolu var mı?

function getSomeString {
   echo "tadaa"
}

VARIABLE=$(getSomeString)

4
Bir kenara, function funcName {POSIX öncesi eski sözdizimi erken ksh'den miras alınır (burada bash'ın onur vermediği anlamsal farklılıklar vardı). yerine funcName() {no functionile kullanılmalıdır; bkz. wiki.bash-hackers.org/scripting/obsolete
Charles Duffy

Bu bağlantıda NAME () COMPOUND-CMD veya NAME {CMDS işlevi kullanıldığını söylüyor; } function myFunction { blah; }İyi de; bu function myFunction() { blah }iyi değildir, yani anahtar kelime işleviyle parantez kullanımı.
Will

Yanıtlar:


290

Bilmemin daha iyi bir yolu yok. Bash yalnızca stdout'a yazılan durum kodlarını (tamsayılar) ve dizeleri bilir.


15
+1 @ tomas-f: "getSomeString ()" fonksiyonunda ne olduğuna gerçekten dikkat etmelisiniz çünkü sonunda yankılanacak herhangi bir koda sahip olmak yanlış dönüş dizesi alacağınız anlamına gelecektir.
Mani

11
Bu sadece yanlış. Bir dönüş değişkeni içinde rastgele veriler döndürebilirsiniz. Hangi açıkça daha iyi bir yoldur.
Evi1M4chine

36
@ Evi1M4chine, um ... hayır, yapamazsın. Komut dosyalarınızda gördüğünüz gibi, genel bir değişken ayarlayabilir ve buna "return" diyebilirsiniz. Ama o zaman konvansiyonla, aslında programla kodunuzun yürütülmesine bağlı DEĞİLDİR. “açıkça daha iyi bir yol” mu? Um, hayır. Komut ikamesi çok daha açık ve modülerdir.
Wildcard

6
"Komut ikamesi çok daha açık ve modülerdir" sorusu komutlarla ilgili olsaydı ilgili olurdu; Bu soru, bash işlevinden bir dizenin nasıl döndürüleceğidir! Bash 4.3'ten (2014?) Beri OP'nin sorduğu şeyi yapmak için yerleşik bir yol var - aşağıdaki cevabımı görün.
zenaan

4
Orijinal soru bunu yapmanın en basit yolunu içerir ve çoğu durumda iyi çalışır. Bash dönüş değerlerine büyük olasılıkla "dönüş kodları" denmelidir çünkü kodlamadaki standart dönüş değerlerine daha az ve daha çok sayısal kabuk komutu çıkış kodlarına benzerler (gibi şeyler yapabilirsiniz somefunction && echo 'success'). Bir işlevi başka bir komut gibi düşünürseniz, mantıklıdır; komutlar çıkışta durum kodu dışında hiçbir şey "döndürmez", ancak bu arada yakalayabileceğiniz şeyler çıktısını verebilir.
Kasım'da Beejor

193

İşlevin bir değişkeni ilk argüman olarak almasını ve değişkeni döndürmek istediğiniz dizeyle değiştirmesini sağlayabilirsiniz.

#!/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

"Foo bar rab oof" yazdırır.

Düzenleme : Dize içindeki boşlukların @Luca Borrione'nin yorumunu ele almasına izin vermek için uygun yere alıntı eklendi.

Düzenleme : Bir gösteri olarak aşağıdaki programa bakın. Bu genel amaçlı bir çözümdür: yerel bir değişkene bir dize almanıza bile izin verir.

#!/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"

Bu yazdırır:

+ 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

Düzenleme : Orijinal değişkenin değeri olduğunu gösteren bir fonksiyonu mevcuttur, hem yanlış bir yorum @Xichen Li tarafından eleştirildi.

#!/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"

Bu çıktı verir:

+ 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
Bu cevap harika! Parametreler C ++ fikrine benzer referanslarla iletilebilir.
Yun Huang

4
Bu yanıtla ilgili olarak bir uzmandan yanıt almak iyi olurdu. Senaryolarda kullanıldığını hiç görmedim, belki de iyi bir nedenden dolayı. Her neyse: bu +1 Doğru cevap için seçilmeliydi
John

Bu fgmbasitleştirilmiş bir şekilde yazılmış cevap ile aynı değil mi? Dize foobeyaz boşluklar içeriyorsa , bu çalışmazken , fgmbir tanesi gösterdiği gibi .. olacaktır.
Luca Borrione

4
@XichenLi: downvote ile yorum yaptığınız için teşekkür ederiz; lütfen düzenlememe bakın. İle fonksiyondaki değişkenin başlangıç ​​değerini alabilirsiniz \$$1. Farklı bir şey arıyorsanız, lütfen bana bildirin.
bstpierre

1
@timiscoding a ile düzeltilebilir printf '%q' "$var". % q kabuk kaçışına yönelik bir biçim dizesidir. Sonra sadece çiğ geçirin.
bb010g

99

Yukarıdaki tüm cevaplar bash'ın man sayfasında belirtilenleri görmezden gelir.

  • Bir işlev içinde bildirilen tüm değişkenler çağıran ortamla paylaşılır.
  • Yerel olarak bildirilen tüm değişkenler paylaşılmaz.

Örnek kod

#!/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

Ve çıktı

$ 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

Ayrıca pdksh ve ksh altında bu script aynı şeyi yapar!


10
Bu cevabın avantajları var. Buraya bir işlevden bir dize döndürmek istediğimi düşünerek geldim. Bu cevap bana bunun sadece benim C # konuşmamın olduğunu anlamamı sağladı. Başkalarının da aynı deneyime sahip olabileceğinden şüpheleniyorum.
LOAS

4
@ElmarZander Yanılıyorsunuz, bu tamamen alakalı. Bu, küresel kapsamı bir işlev kapsamı değeri elde etmenin basit bir yoludur ve bazıları bunu bstpierre tarafından ana hatlarıyla belirtildiği gibi küresel bir değişkeni yeniden tanımlamak için eval yaklaşımından daha iyi / basit olarak düşünür.
KomodoDave

local, bazı insanların bundan kaçınmasının bir nedeni olan bash olmayan komut dosyalarına taşınabilir değildir.
parlak

Soru: Döngülerdeki değişkenler ne olacak?
anu

1
Mac'te ($ bash --version GNU bash, sürüm 3.2.57 (1) -release (x86_64-apple-darwin14) Telif Hakkı (C) 2007 Free Software Foundation, Inc.), eşleşen bir global değişkenin başlatıldı, ancak aynı değişkeni başka bir işlev f2'de yan etki yapmaya çalıştığımda, bu yan etki devam etmiyor. Yani, çok tutarsız görünüyor ve bu nedenle kullanımım için iyi değil.
AnneTheAgile

45

Bash, sürüm 4.3, Şubat 2014 (?) 'Den bu yana, "eval" ın ötesinde, aynı faydalı performans ve dolaylı etki ile referans değişkenleri veya ad referansları (namerefs) için açık bir desteğe sahiptir ve bunlar komut dosyalarınızda daha net ve daha zor olabilir "'değerlendirmeyi' unut ve bu hatayı düzeltmek zorunda":

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...

ve ayrıca:

PARAMETRELERİ

Bir değişken veya başka bir değişkene bir başvuru oluşturmak için, değişkene declare veya yerel yerleşik komutlara -n seçeneği (aşağıda declare ve local açıklamalarına bakın) kullanılarak -bir seçeneği kullanılarak nameref niteliği atanabilir. Bu, değişkenlerin dolaylı olarak değiştirilmesine izin verir. Nameref değişkenine her başvuruda veya atandığında, işlem aslında nameref değişkeninin değeri tarafından belirtilen değişken üzerinde gerçekleştirilir. Bir nameref, kabuk işlevleri içinde, adı işleve argüman olarak iletilen bir değişkeni ifade etmek için yaygın olarak kullanılır. Örneğin, bir değişken adı kabuk işlevine ilk argümanı olarak iletilirse,

      declare -n ref=$1

işlevin içinde, değeri ilk bağımsız değişken olarak iletilen değişken adı olan bir nameref değişkeni oluşturur. Ref'ye yapılan referanslar ve atamalar, adı $ 1 olarak geçen değişkene referanslar ve atamalar olarak kabul edilir. For döngüsünde kontrol değişkeni nameref özniteliğine sahipse, sözcük listesi kabuk değişkenlerinin bir listesi olabilir ve döngü yürütüldüğünde listedeki her sözcük için bir ad başvurusu oluşturulacaktır. Dizi değişkenlerine -n ​​özelliği verilemez. Ancak, nameref değişkenleri dizi değişkenlerine ve abone dizi değişkenlerine başvurabilir. Aderefs, unset yerleşiminin -n seçeneği kullanılarak ayarlanamaz. Aksi takdirde, unset bağımsız değişken olarak bir nameref değişkeninin adıyla yürütülürse,

Örneğin ( DÜZENLEME 2 : (teşekkürler Ron)), nihayet doğru cevap vermesi gereken dış değişken çakışmalarını en aza indirmek için işlev-dahili değişken adını adlandırdı (önekledi), Karsten tarafından yapılan yorumlarda ortaya çıkan sorun):

# $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
}

ve bu örneği test etmek:

$ return_a_string result; echo $result
The date is 20160817

Bir işlevde kullanıldığında bash "declare" yerleşiminin varsayılan olarak bildirilen değişkeni "local" hale getirdiğini ve "-n" ifadesinin de "local" ile kullanılabileceğini unutmayın.

"Önemli deklarasyon" değişkenlerini "sıkıcı yerel" değişkenlerden ayırmayı tercih ederim, bu yüzden "deklare" ve "yerel" kelimelerini bu şekilde kullanmak dokümantasyon görevi görür.

DÜZENLEME 1 - (Karsten tarafından aşağıdaki yoruma verilen yanıt) - Daha fazla aşağıya yorum ekleyemiyorum, ancak Karsten'in yorumu beni düşündürdü, bu yüzden eğer okuduysanız, HASSAS - AFAICT - Karsten'i çalıştıran aşağıdaki testi yaptım, lütfen kesin bir set sağlayın Aşağıdaki adımların iyi çalıştığı için komut satırından test adımlarının var olduğunu varsayalım.

$ return_a_string ret; echo $ret
The date is 20170104

(Bunu hemen şimdi, yukarıdaki işlevi bir bash terimine yapıştırdıktan sonra - gördüğünüz gibi, sonuç gayet iyi çalışıyor.)


4
Ümit ediyorum ki bunun zirveye çıkması. eval son çare olmalı. Bahsetmeye değer, nameref değişkenlerinin sadece bash 4.3'ten ( changelog'a göre ) (2014 Şubat ayında piyasaya sürülen [?]) Piyasada mevcut olduğudur . Taşınabilirlik bir endişe kaynağı ise bu önemlidir. Lütfen bash el kitabını declareişlevler içinde yerel değişkenler yarattığı gerçeğine (bu bilgi tarafından verilmez help declare): "... Bir işlevde kullanıldığında, bildirmek ve dizgi her komutu yerel komutla olduğu gibi yerel komutla olduğu gibi yerel yapın. g seçeneği sağlanır ... "
init_js

2
Bu, eval çözümüyle aynı örtüşme problemine sahiptir. Bir işlevi çağırıp çıktı değişkeninin adını ilettiğinizde, yerel olarak kullanılan bir değişkenin adını çağırdığınız işlev içinde iletmekten kaçınmanız gerekir. Bu, kapsülleme açısından önemli bir sorundur, çünkü arayanların işlevlerden herhangi biri çıkış parametresi için bu adı kullanmak istemeden bir işleve yeni yerel değişkenler ekleyemez veya yeniden adlandıramazsınız.
Karsten

1
@Karsten kabul etti. her iki durumda da (eval ve namerefs) farklı bir isim seçmeniz gerekebilir. Değerlendirme üzerinde nameref yaklaşımının bir avantajı, kaçan dizelerle uğraşmak zorunda olmamasıdır. Tabii ki, her zaman gibi bir şey yapabilirsiniz K=$1; V=$2; eval "$A='$V'";, ancak bir hata (örneğin boş veya atlanmış bir parametre) ve daha tehlikeli olacaktır. @zenaan, "ret" yerine dönüş değişkeni adı olarak "message" ı seçerseniz, @Karsten tarafından ortaya çıkan sorun geçerlidir.
init_js

3
Bir işlev muhtemelen bir aderef argümanını kabul edecek şekilde tasarlanmalıdır, bu nedenle işlev yazarı bir ad çakışması olasılığının farkında olmalı ve bundan kaçınmak için bazı tipik kurallar kullanabilir. Örneğin, X fonksiyonu içinde, yerel değişkenleri "X_LOCAL_name" kuralıyla adlandırın.
Ron Burk

34

Gibi bstpierre yukarıda kullandığım ve açıkça adlandırma çıktı değişkenleri kullanımını tavsiye:

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
}

$ Tırnak işareti kullanımını unutmayın. Bu, içeriği $resultkabuk özel karakterleri olarak yorumlamaktan kaçınacaktır . Bunun bir olduğunu bulduk hızlı büyüklük sırası daha result=$(some_func "arg1")yankı yakalama deyim. Hız farkı, işlev çağrılarından yakalanan stdout'un neredeyse felaket olduğu MSYS'de bash kullanıldığında daha da dikkat çekici görünüyor.

Yerel değişkenler bash içinde dinamik olarak kapsandığından yerel değişkenler göndermek uygun:

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

4
Hata ayıklama / günlüğe kaydetme amacıyla birden çok yankı deyimlerini kullanmayı sevdiğim için bu bana yardımcı olur. Yankı yakalama deyimi hepsini yakaladığından başarısız olur. Teşekkür ederim!
AnneTheAgile

Bu (en iyi ikinci) uygun çözümdür! Temiz, hızlı, zarif, mantıklı.
Evi1M4chine

+2 gerçek tutmak için. Söylemek üzereydim. Bu kadar çok insan echobir fonksiyonun içini komuta yerine koymakla birleştirmeyi nasıl görmezden gelebilir !
Anthony Rutledge

23

İşlev çıktısını da yakalayabilirsiniz:

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

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

Tuhaf görünüyor, ancak global değişkenler IMHO'yu kullanmaktan daha iyi. Parametreleri geçmek her zamanki gibi çalışır, bunları parantez veya ters tırnakların içine koyun.


11
alternatif sözdizimi notunun dışında, bu op'un zaten kendi sorusunda yazdığı şeyle aynı değil mi?
Luca Borrione

çok açık, teşekkürler!
bcag2

12

En basit ve sağlam çözüm, diğer insanların yazdığı gibi komut ikamesini kullanmaktır:

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

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

Dezavantajı, ayrı bir işlem gerektirdiğinden performanstır.

Bu konuda önerilen diğer tekniğin, bağımsız değişken olarak atanacak bir değişkenin adını geçmesinin yan etkileri vardır ve bunu temel biçiminde önermem. Sorun, muhtemelen dönüş değerini hesaplamak için işlevde bazı değişkenlere ihtiyaç duyacağınız ve dönüş değerini depolamak için kullanılan değişkenin adının bunlardan birine müdahale edeceği olabilir:

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

Tabii ki, işlevin iç değişkenlerini yerel olarak bildiremezsiniz, ancak bunu her zaman yapmalısınız, aksi takdirde, aynı ada sahip bir tane varsa, yanlışlıkla ana kapsamdan ilgisiz bir değişkenin üzerine yazabilirsiniz. .

Olası bir geçici çözüm, geçirilen değişkenin genel olarak açık bir beyanıdır:

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

"X" adı bağımsız değişken olarak iletilirse, işlev gövdesinin ikinci satırı önceki yerel bildirimin üzerine yazılır. Ancak isimlerin kendileri yine de müdahale edebilir, bu nedenle oraya dönüş değerini yazmadan önce geçirilen değişkende daha önce saklanan değeri kullanmayı düşünüyorsanız, başlangıçta başka bir yerel değişkene kopyalamanız gerektiğini unutmayın; aksi halde sonuç tahmin edilemez olacaktır! Ayrıca, bu sadece BASH'in en son sürümü olan 4.2'de çalışacaktır. Daha taşınabilir kodlar, aynı etkiye sahip açık koşullu yapıları kullanabilir:

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

Belki de en şık çözüm, işlev dönüş değerleri için tek bir global ad ayırmak ve bunu yazdığınız her işlevde tutarlı bir şekilde kullanmaktır.


3
Bu ^ ^ ^. O sonları encapsulation aliasing istemeden hem büyük bir sorundur evalve declare -nçözümleri. resultTüm çıkış parametreleri gibi tek bir özel değişken adına sahip olmanın çözümü, çakışmalardan kaçınmak için tüm arayanları tanımak için bir işlev gerektirmeyen tek çözüm gibi görünüyor.
Karsten

12

Daha önce de belirtildiği gibi, bir işlevden bir dize döndürmenin "doğru" yolu komut yerine kullanılır. Ayrıca işlevin konsola çıkışı olması gerektiğinde (yukarıdaki @Mani'den bahsedildiği gibi), işlevin başlangıcında geçici bir fd oluşturun ve konsola yeniden yönlendirin. Dizenizi döndürmeden önce geçici fd'yi kapatın.

#!/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]"

hiçbir parametre ile komut dosyası yürütme üretir ...

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

umarım bu insanlara yardım eder

-Andy


6
Bunun kullanımları vardır, ancak genel olarak konsola açık bir yönlendirme yapmaktan kaçınmalısınız; çıktı zaten yeniden yönlendirilmiş olabilir veya komut dosyası, tty'nin bulunmadığı bir bağlamda çalışıyor olabilir. 3>&1Komut dosyasının başında çoğaltarak , sonra işlev içinde &1 &3başka bir yer tutucuyu manipüle ederek bunu başarabilirsiniz &4. Yine de çirkin.
jmb

8

Genel bir değişken kullanabilirsiniz:

declare globalvar='some string'

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

string globalvar

echo "'${globalvar}'"

Bu verir

'some other string'

6

Kullanmaktan kaçınmak için ek dosya tanımlayıcı manipülasyonu ile Andy'nin cevabı hakkındaki yorumumu göstermek için /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]"

Yine de kötü.


3

Sahip olduğunuz yol, kapsamı bozmadan bunu yapmanın tek yoludur. Bash'in dönüş türleri kavramı yoktur, sadece çıkış kodları ve dosya tanımlayıcıları (stdin / out / err, vb.)


3

Adresleme Vicky Ronnen aşağıdaki kodu göz önünde bulundurarak 'ın baş yukarı:

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



verecek

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

Belki de normal senaryo, test_inside_a_funcişlevde kullanılan sözdizimini kullanmaktır , bu nedenle, çoğu durumda her iki yöntemi de kullanabilirsiniz, ancak çıktıyı yakalamak her zaman her durumda çalışan daha güvenli bir yöntemdir, geri dönüş değerini taklit edebileceğiniz bir işlevden taklit eder. diğer dillerde, Vicky Ronnendoğru bir şekilde işaret edildiği gibi bulun .


2

Bence tüm seçenekler numaralandırıldı. Birini seçmek, özel uygulamanız için en iyi stil konusuna gelebilir ve bu bağlamda, yararlı bulduğum belirli bir stil sunmak istiyorum. Bash'de, değişkenler ve işlevler aynı ad alanında değildir. Bu nedenle, işlevin değeriyle aynı addaki değişkenin ele alınması, titizlikle uygularsam, ad çakışmalarını en aza indirgeyen ve okunabilirliği artıran bir kuraldır. Gerçek hayattan bir örnek:

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"
}

Ve bu tür işlevlerin kullanımına bir örnek:

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

Gördüğünüz gibi, dönüş durumu ihtiyacınız olduğunda kullanmanız için veya orada yoksa görmezden gelmeniz için orada. "Döndürülen" değişken de benzer şekilde kullanılabilir veya yok sayılabilir, ancak elbette yalnızca işlev çağrıldıktan sonra .

Tabii ki, bu sadece bir kongre. Döndürmeden önce ilişkili değeri ayarlayamazsınız (bu nedenle işlevin başında her zaman nulling yapma kuralım) veya işlevi tekrar çağırarak (muhtemelen dolaylı olarak) değerini çiğnemekte özgürsünüz. Yine de, kendimi bash işlevlerinden yoğun şekilde yararlanırsam çok yararlı bulduğum bir kongre.

Bunun bir işaret olduğu düşüncesinin aksine, örneğin "perl'e taşınması" felsefem, her dilin karmaşıklığını yönetmek için sözleşmelerin her zaman önemli olduğu yönündedir.


2

echoBir dize olabilir , ancak |işlevi başka bir şeye piping ( ) yaparak yakalayabilirsiniz .

ShellCheck bu kullanımı kullanımdan kaldırılmış olarak bildirmesine exprrağmen bunu yapabilirsiniz .


Sorun şu ki, borunun sağındaki şey bir alt kabuk. Yani myfunc | read OUTPUT ; echo $OUTPUThiçbir şey vermez. myfunc | ( read OUTPUT; echo $OUTPUT )beklenen değeri alır ve sağ tarafta neler olduğunu açıklar. Ama elbette OUTPUT ihtiyacınız olan yerde mevcut değil ...
Ed Randall

2

Onlar Arayan (kullanarak ister değişken adında geçebilir herhangi bir 'isimli çıkış değişkeni' şemasının anahtar sorun evalya declare -n) kasıtsız aliasing, yani adı çatışmaları geçerli: Bakış bir kapsülleme açıdan bakıldığında, eklemek veya yeniden adlandırma mümkün olmayabilir etmek zor çıkış parametresiyle aynı adı iletmek istemediklerinden emin olmak için önce TÜM işlevin arayanlarını denetlemeden bir işlevdeki yerel değişken . (Ya da diğer yönde, sadece kullanmak istediğim çıkış parametresinin bu işlevde bir yerel olmadığından emin olmak için aradığım işlevin kaynağını okumak istemiyorum.)

Bunun tek yolu, REPLY( Evi1M4chine tarafından önerildiği gibi) gibi tek bir özel çıktı değişkeni veya Ron Burk tarafından önerilen gibi bir kongre kullanmaktır .

Ancak, işlevlerin sabit bir çıktı değişkeni kullanması mümkündür aşağıdaki örnekte işlevle yaptığım gibi, işlevlerin dahili olarak ve ardından bu gerçeği arayandan gizlemek için üstüne biraz şeker eklemek mümkündür call. Bunu bir kavram kanıtı olarak düşünün, ancak kilit noktalar

  • İşlev her zaman dönüş değerini atar ve REPLYher zamanki gibi bir çıkış kodu döndürebilir
  • Arayanın bakış açısından, dönüş değeri dahil olmak üzere herhangi bir değişkene (yerel veya global) atanabilir REPLY( wrapperörneğe bakın ). Fonksiyonun çıkış kodu böylece örneğin bunları kullanarak geçirilir ifveya whileveya benzer yapılar beklendiği gibi çalışır.
  • Sözdizimsel olarak işlev çağrısı hala tek bir basit ifadedir.

Bunun nedeni, callişlevin kendisinin yerel yeri olmaması ve REPLYad çakışmaları için herhangi bir potansiyelden kaçınmak dışında hiçbir değişken kullanmamasıdır . Arayan tanımlı çıkış değişkeni adının atandığı noktada, etkin bir şekilde arayanın kapsamındayız (teknik olarakcall , çağrılan işlevin kapsamından ziyade, işlevin ).

#!/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

Çıktı:

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

1

Bash desen her dönüş skaler ve dizi değeri nesneleri:

tanım

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
}

yakarma

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

Programlarımda, kural gereği, önceden varolan $REPLYdeğişkenin readamacı budur ve bu da tam olarak bu amaca yöneliktir.

function getSomeString {
  REPLY="tadaa"
}

getSomeString
echo $REPLY

Bu echoes

tadaa

Ancak çatışmalardan kaçınmak için diğer küresel değişkenler yapacak.

declare result

function getSomeString {
  result="tadaa"
}

getSomeString
echo $result

Bu yeterli değilse Markarian451'in çözümünü öneririm .


-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=
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.