Değişken genişleme nasıl ertelenir


18

Komut dizimin üstündeki bazı dizeleri henüz ayarlanmamış değişkenlerle başlatmak istiyordum, örneğin:

str1='I went to ${PLACE} and saw ${EVENT}'
str2='If you do ${ACTION} you will ${RESULT}'

ve daha sonra da PLACE, EVENT, ACTIONve RESULTkurulacaktır. Daha sonra değişkenler genişletilmiş ile benim dizeleri yazdırmak istiyorum. Tek seçeneğim evalmi? Bu işe yarıyor gibi görünüyor:

eval "echo ${str1}"

bu standart mı? Bunu yapmanın daha iyi bir yolu var mı? evalDeğişkenlerin herhangi bir şey olabileceğini düşünerek çalışmamak hoş olurdu .

Yanıtlar:


23

Gösterdiğiniz girdi türüyle, değerlerin bir dizginin yerine geçmesi için kabuk genişlemesinden yararlanmanın tek yolu evalbazı biçimlerde kullanmaktır . Bu, değerini kontrol ettiğiniz sürece güvenlidir str1ve yalnızca güvenli olarak bilinen (gizli veriler içermeyen) değişkenlere başvurduğundan ve başka bir alıntı yapılmayan özel kabuk karakteri içermediğinden emin olabilirsiniz. Dizeyi çift tırnak işaretleri içinde veya buradaki bir belgede genişletmelisiniz, bu şekilde yalnızca "$\`özeldir (öncesinde bir \inç olması gerekir str1).

eval "substituted=\"$str1\""

Dize yerine bir işlev tanımlamak çok daha sağlam olurdu.

fill_template () {
  sentence1="I went to ${PLACE} and saw ${EVENT}"
  sentence2="If you do ${ACTION} you will ${RESULT}"
}

Değişkenleri ayarlayın ve sonra fill_templateçıkış değişkenlerini ayarlamak için işlevi çağırın .

PLACE=Sydney; EVENT=fireworks
ACTION='not learn from history'; RESULT='have to relive history'
fill_template
echo "During my holidays, $sentence1."
echo "Cicero said: \"$sentence2\"."

2
Değerlendirmeyi geciktirmek ve açık değerlendirme çağrısından kaçınmak için bir işlev kullanarak güzel bir çalışma.
Clayton Stanley

İyi çözüm, bu bana çok yardımcı oldu. Teşekkürler!
Stuart

8

Anlamını alırken, bu cevapların hiçbirinin doğru olduğuna inanmıyorum. evalhiçbir şekilde gerekli değildir ve değişkenlerinizi iki kez değerlendirmenize bile gerek yoktur.

Doğru, @Gilles çok yaklaşıyor, ancak değerleri geçersiz kılma sorununu ve birden fazla ihtiyacınız varsa nasıl kullanılması gerektiğini ele almıyor. Sonuçta, bir şablon birden fazla kullanılmalıdır, değil mi?

Bence bu onları değerlendirdiğiniz sıra önemli. Aşağıdakileri göz önünde bulundur:

ÜST

Burada bazı varsayılanlar ayarlayacak ve çağrıldığında yazdırmaya hazırlanacaksınız ...

#!/bin/sh
    _top_of_script_pr() ( 
        IFS="$nl" ; set -f #only split at newlines and don't expand paths
        printf %s\\n ${strings}
    ) 3<<-TEMPLATES
        ${nl=
}
        ${PLACE:="your mother's house"}
        ${EVENT:="the unspeakable."}
        ${ACTION:="heroin"}
        ${RESULT:="succeed."}
        ${strings:="
            I went to ${PLACE} and saw ${EVENT}
            If you do ${ACTION} you will ${RESULT}
        "}
    #END
    TEMPLATES

ORTA

Bu, sonuçlarına göre yazdırma işlevinizi çağırmak için diğer işlevleri tanımladığınız yerdir ...

    EVENT="Disney on Ice."
    _more_important_function() { #...some logic...
        [ $((1+one)) -ne 2 ] && ACTION="remedial mathematics"
            _top_of_script_pr
    }
    _less_important_function() { #...more logic...
        one=2
        : "${ACTION:="calligraphy"}"
        _top_of_script_pr
    }

ALT

Artık her şeyi ayarladınız, işte burada sonuçlarınızı uygulayacak ve çekeceksiniz.

    _less_important_function
    : "${PLACE:="the cemetery"}" 
    _more_important_function
    : "${RESULT:="regret it."}" 
    _less_important_function    

SONUÇLAR

Nedenini birazdan anlatacağım, ancak yukarıdakileri çalıştırmak aşağıdaki sonuçları üretir:

_less_important_function()'s ilk çalıştırma:

Annenin evine gittim ve Disney on Ice'ı gördüm .

Bunu yaparsanız kaligrafiyi başarılı olacaktır.

sonra _more_important_function():

Mezarlığa gittim ve Disney on Ice'ı gördüm.

Bunu yaparsanız iyileştirici matematik başarılı olacaktır.

_less_important_function() tekrar:

Mezarlığa gittim ve Disney on Ice'ı gördüm.

Düzeltici matematik yaparsanız pişman olacaksınız .

NASIL ÇALIŞIR:

Buradaki temel özellik, conditional ${parameter} expansion.bir değişkeni bir değere yalnızca form kullanılarak ayarlanmamış veya boşsa ayarlayabilirsiniz:

${var_name: =desired_value}

Bunun yerine yalnızca ayarlanmamış bir değişken ayarlamak isterseniz, :colonve null değerleri olduğu gibi kalacaktır.

KAPSAMDA:

Yukarıdaki örnekte fark edilebilir $PLACEve zaten çağrılmış olsa bile $RESULTüzerinden ayarlandığında değişebilir , muhtemelen çalıştırıldığında onları ayarlayabilirsiniz. Bunun işe yaramasının nedeni bir işlevdir - diğerleri için kullanılandan ziyade içine aldım . Alt kabukta çağrıldığından, ayarladığı her değişken üst kabuğuna döndüğünde bu değerler kaybolur.parameter expansion_top_of_script_pr()_top_of_script_pr()( subshelled )parens{ curly braces }locally scoped

Ancak _more_important_function()setler $ACTIONolduğu zaman , ikinci setin değerlendirmesini globally scopedetkiler çünkü setler sadece_less_important_function()'s$ACTION_less_important_function()$ACTION${parameter:=expansion}.

:BOŞ

Ve lider kullanmak niçin :colon?Kuyusu, mansayfa söyleyecektir : does nothing, gracefully.Görüyorsunuz, parameter expansiono - bu gibi sesler tam olarak ne expandsdeğerine ${parameter}.So ile bir değişkeni ayarladığınızda ${parameter:=expansion}biz değeri ile sol - hangi kabuk irade satır içi yürütmeyi deneyin. Çalıştırmaya çalıştıysa the cemetery, size bazı hatalar tükürecekti. PLACE="${PLACE:="the cemetery"}"aynı sonuçları üretirdi, ancak bu durumda da gereksizdir ve kabuğun: ${did:=nothing, gracefully}.

Bunu yapmanıza izin verir:

    echo ${var:=something or other}
    echo $var
something or other
something or other

BURADA-BELGELER

Ve bu arada - null veya unset değişkeninin satır içi tanımı da aşağıdakilerin neden işe yaradığıdır:

    <<HEREDOC echo $yo
        ${yo=yoyo}
    HEREDOC
yoyo

Bir düşünmenin en iyi yolu here-document A'yı girdi dosya tanımlayıcısına akış olarak gerçek bir dosyadır. Aşağı yukarı bunlar budur, ancak farklı mermiler onları biraz farklı uygular.

Her durumda, teklif vermezseniz <<LIMITER bunu bir şekilde akmaktadır olsun ve için değerlendirilir expansion.bir bir değişkeni bildirerek So here-documentteneke işleri, ancak yalnızca yoluyla expansionhangi yalnızca önceden ayarlanmamış değişkenleri ayarlayarak sizi sınırlar. Yine de, tanımladığınız gibi ihtiyaçlarınıza tam olarak uyar, çünkü şablon yazdırma işlevinizi çağırdığınızda varsayılan değerleriniz her zaman ayarlanır.

NEDEN OLMASIN eval?

Sunmuş olduğum örnek, kabul etmenin güvenli ve etkili bir parameters.yolunu sunar Kapsamı işlediğinden, üzerinden ayarlanan her değişken ${parameter:=expansion}dışarıdan tanımlanabilir. Yani, tüm bunları template_pr.sh adlı bir komut dosyasına koyarsanız ve çalıştırırsanız:

 % RESULT=something_else template_pr.sh

Şunları elde edersiniz:

Annenin evine gittim ve Disney on Ice'ı gördüm

Kaligrafi yaparsanız bir şey yaparsınız

Mezarlığa gittim ve Disney on Ice'ı gördüm

Düzeltici matematik yaparsanız

Mezarlığa gittim ve Disney on Ice'ı gördüm

Düzeltici matematik yaparsanız

Bu, komut dosyasında tam anlamıyla ayarlanan değişkenler için işe yaramaz, örneğin $EVENT, $ACTION, ve $one,ancak ben sadece farkı göstermek için bu şekilde tanımladım.

Her durumda, bir evaledifadeye bilinmeyen girdilerin kabulü doğal olarak güvensizdir, oysa parameter expansionbunu yapmak için özel olarak tasarlanmıştır.


1

Genişletilmemiş değişkenler yerine dize şablonları için yer tutucular kullanabilirsiniz. Bu oldukça çabuk dağınık olacak. Yaptığınız şey çok şablon ağırsa, gerçek bir şablon kütüphanesi olan bir dili düşünmek isteyebilirsiniz.

format_template() {
    changed_str=$1

    for word in $changed_str; do
        if [[ $word == %*% ]]; then
            var="${word//\%/}"
            changed_str="${changed_str//$word/${!var}}"
        fi
    done
}

str1='I went to %PLACE% and saw %EVENT%'
PLACE="foo"
EVENT="bar"
format_template "$str1"
echo "$changed_str"

Yukarıdakilerin dezavantajı, şablon değişkeninin kendi sözcüğü olması gerektiğidir (örneğin yapamazsınız "%prefix%foo"). Bu, bazı değişikliklerle veya dinamik olarak şablon değişkeninin sabit kodlanmasıyla düzeltilebilir.

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.