Bash'te eval komutu ve tipik kullanımları


166

Bash adam sayfalarını okuduktan sonra ve bu bakımından yazı .

Hala evalkomutun tam olarak ne yaptığını ve tipik kullanımlarının ne olacağını anlamakta zorlanıyorum . Örneğin;

bash$ set -- one two three  # sets $1 $2 $3
bash$ echo $1
one
bash$ n=1
bash$ echo ${$n}       ## First attempt to echo $1 using brackets fails
bash: ${$n}: bad substitution
bash$ echo $($n)       ## Second attempt to echo $1 using parentheses fails
bash: 1: command not found
bash$ eval echo \${$n} ## Third attempt to echo $1 using 'eval' succeeds
one

Burada tam olarak neler oluyor ve dolar işareti ve ters eğik çizgi soruna nasıl bağlanıyor?


1
Kayıt için ikinci girişim işe yarıyor. bir alt kabukta $($n)çalışır $n. 1Mevcut olmayan komutu çalıştırmayı dener .
Martin Wickman

1
@MartinWickman Ama gereksinim echo $1sonunda çalışmaktır, değil 1. Alt kabuklar kullanılarak yapılabileceğini sanmıyorum.
Hari Menon


1
@ Raze2dust: Alt kabuklarla çalıştırılabileceğini öne sürdüğüne inanmıyorum, aksine listelenen OP'nin 5. komutunun neden çalışmadığını açıklıyorum.
jedwards

Yanıtlar:


196

evalbir dizeyi bağımsız değişkeni olarak alır ve dizeyi bir komut satırına yazmış gibi değerlendirir. (Birkaç argüman iletirseniz, ilk önce aralarındaki boşluklarla birleştirilirler.)

${$n}bash dilinde bir sözdizimi hatasıdır. Parantezlerin içinde, yalnızca bazı olası önek ve soneklerle bir değişken adınız olabilir, ancak rastgele bash sözdizimine sahip olamazsınız ve özellikle değişken genişletmeyi kullanamazsınız. Yine de “adı bu değişkente olan değişkenin değeri” demenin bir yolu vardır:

echo ${!n}
one

$(…)parantez içinde belirtilen komutu bir alt kabukta (yani geçerli kabuktan değişken değerler gibi tüm ayarları devralan ayrı bir işlemde) çalıştırır ve çıktısını toplar. Bu yüzden bir kabuk komutu olarak echo $($n)çalışır $nve çıktısını görüntüler. Yana $nüzere değerlendirir 1, $($n)komutu çalıştırmak için girişimleri 1yok.

eval echo \${$n}geçirilen parametreleri çalıştırır eval. Genişletmeden sonra parametreler echove şeklindedir ${1}. Böylece eval echo \${$n}komutu çalıştırır echo ${1}.

Çoğu zaman, değişken ikameler ve komut ikameleri etrafında çift tırnak kullanmanız gerektiğini unutmayın (örn . A olduğunda $) :. Bunları bırakmanız gerektiğini bilmiyorsanız, değişken ve komut ikamelerine her zaman çift tırnak koyun . Çift tırnak işaretleri olmadan, kabuk alan ayırma gerçekleştirir (yani değişkenin değerini veya komuttan çıktıyı ayrı sözcüklere ayırır) ve sonra her sözcüğü bir joker karakter deseni olarak ele alır. Örneğin:"$foo", "$(foo)"

$ ls
file1 file2 otherfile
$ set -- 'f* *'
$ echo "$1"
f* *
$ echo $1
file1 file2 file1 file2 otherfile
$ n=1
$ eval echo \${$n}
file1 file2 file1 file2 otherfile
$eval echo \"\${$n}\"
f* *
$ echo "${!n}"
f* *

evalçok sık kullanılmaz. Bazı kabuklarda en yaygın kullanım, çalışma zamanına kadar adı bilinmeyen bir değişkenin değerini elde etmektir. Bash'da, ${!VAR}sözdizimi sayesinde bu gerekli değildir . evalişleçleri, ayrılmış kelimeleri vb. içeren daha uzun bir komut oluşturmanız gerektiğinde hala yararlıdır.


Yukarıdaki yorumumla ilgili olarak, eval kaç "geçiş" yapıyor?
kstratis

@ Konos5 Ne yorum? eval(kendisi ayrıştırma ve değerlendirmenin sonucu olabilir) bir dize alır ve onu bir kod snippet'i olarak yorumlar.
Gilles 'SO- kötü olmayı kes'

Raze2dust'un cevabı altında bir yorum bıraktım. Şimdi eval'ın çoğunlukla kayıt dışı bırakma amacıyla kullanıldığına inanıyorum. Eval echo \ $ {$ n} yazarsam bir tane alırım. Ancak echo \ $ {$ n} yazarsam \ $ {1} alırım. Bunun eval'ün "iki geçişli" ayrıştırılması nedeniyle olduğuna inanıyorum. Şimdi ben ekstra bir i = n beyanı kullanarak üçlü dereference gerekiyorsa ne olacağını merak ediyorum. Bu durumda Raze2dust'a göre sadece fazladan bir değerlendirme yapmam gerekiyor. Ancak daha iyi bir yol olması gerektiğine inanıyorum ... (kolayca
karışabilir

@ Konos5 Kullanmazdım eval eval. Gereksinimi hissettiğini hatırlayamıyorum. Eğer gerçekten iki gerekiyorsa evalgeçer, geçici değişken kullanın, hata ayıklamak daha kolay olacak: eval tmp="\${$i}"; eval x="\${$tmp}".
Gilles 'SO- kötü olmayı kes'

1
@ Konos5 "İki kez ayrıştırıldı" biraz yanıltıcı. Bazı insanlar, Bash'de çeşitli açılımlardan korunan gerçek bir dize argümanı belirtmenin zorluğu nedeniyle buna inanmaya yönlendirilebilir. evalkodu bir dizede alır ve normal kurallara göre değerlendirir. Teknik olarak, bu doğru bile değil, çünkü Bash'in ayrıştırmayı eval argümanlarına genişletme yapmak için bile değiştirmediği birkaç köşe vakası var - ama bu kimsenin bildiğinden şüphelendiğim çok belirsiz bir tidbit.
ormaaj

39

Değerlendirmeyi "ifadenizi yürütmeden bir kez önce değerlendirmek" olarak düşünün.

eval echo \${$n}echo $1değerlendirmenin ilk turundan sonra olur . Dikkat edilmesi gereken üç değişiklik:

  • \$Oldu $(tersbölü'ye ihtiyaç vardır, aksi takdirde değerlendirmeye çalışır ${$n}adında bir değişken, yani {$n}izin verilmez)
  • $n için değerlendirildi 1
  • evalkayboldu

İkinci turda, temel echo $1olarak doğrudan uygulanabilir.

Bu yüzden eval <some command>önce değerlendirir <some command>(burada değerlendirmek yerine ikame değişkenleri kastediyorum, kaçan karakterleri doğru olanlarla değiştiririm vb.)

evaldinamik olarak değişkenler oluşturmak veya özel olarak bu şekilde okunacak şekilde tasarlanmış programlardan çıktıları okumak istediğinizde kullanılır. Örnekler için bkz. Http://mywiki.wooledge.org/BashFAQ/048 . Bağlantı aynı zamanda evalkullanılan bazı tipik yolları ve bununla ilişkili riskleri de içerir .


3
Birinci mermi için bir not olarak, ${VAR}söz dizimi olan , herhangi bir belirsizlik (mu olduğu zaman izin verilir ve tercih edilen $VAR == $V, takip ARveya $VAR == $VAardından R). ${VAR}eşittir $VAR. Aslında, bunun değişken ismine $nizin verilmez.
jedwards

2
eval eval echo \\\${\${$i}}üçlü dereference yapacaktır. Bunu yapmanın daha basit bir yolu olup olmadığından emin değilim. Ayrıca, makinemde \${$n}iyi çalışıyor (baskı one) ..
Hari Menon

2
@ Konos5 echo \\\${\${$i}}yazdırır \${${n}}. $ {1} eval echo \\\${\${$i}}ile eşdeğer eval eval echo \\\ $ {\ $ {$ i}} `ile eşdeğerdir ve yazdırır . echo \${${n}}`` and prints . eval echo ${1}one
Gilles 'SO- kötü olmayı kes'

2
@ Konos5 Aynı çizgileri düşünün - İlk ` escapes the second one, and the third ` $bundan sonra kaçar . Böylece \${${n}}bir değerlendirme turundan sonra olur
Hari Menon

2
@ Konos5 Soldan sağa, alıntı ve ters eğik çizgi ayrıştırması için düşünmenin doğru yolu. İlk önce \\ bir ters eğik çizgi verdi. Sonra \$bir dolar verdi. Ve bunun gibi.
Gilles 'SO- kötü olmayı kes

26

Deneyimlerime göre, eval "tipik" kullanımı ortam değişkenleri ayarlamak için kabuk komutları üreten komutları çalıştırmak içindir.

Belki de ortam değişkenlerinin bir koleksiyonunu kullanan bir sisteminiz var ve hangilerinin ayarlanacağını ve değerlerini belirleyen bir komut dosyanız veya programınız var. Bir komut dosyasını veya programı her çalıştırdığınızda, çatallı bir işlemde çalışır, bu nedenle doğrudan ortam değişkenlerine yaptığı her şey çıktıkça kaybolur. Ancak bu komut dosyası veya program dışa aktarma komutlarını stdout'a gönderebilir.

Eval olmadan, stdout'u geçici bir dosyaya yönlendirmeniz, geçici dosyayı kaynaklamanız ve ardından silmeniz gerekir. Eval ile şunları yapabilirsiniz:

eval "$(script-or-program)"

Tırnak işaretleri önemlidir. Bu (tutarlı) örneği ele alalım:

# activate.sh
echo 'I got activated!'

# test.py
print("export foo=bar/baz/womp")
print(". activate.sh")

$ eval $(python test.py)
bash: export: `.': not a valid identifier
bash: export: `activate.sh': not a valid identifier
$ eval "$(python test.py)"
I got activated!

bunu yapan ortak araçlara örnek var mı? Aracın kendisi, eval'e geçirilebilen bir dizi kabuk komutu üretmek için bir araca sahiptir?
Joakim Erdfelt

@Joakim Bunu yapan herhangi bir açık kaynak aracı bilmiyorum, ancak çalıştığım şirketlerde bazı özel komut dosyalarında kullanıldı. Bu tekniği tekrar xampp ile kullanmaya başladım. Apache .conf dosyaları yazılan ortam değişkenlerini genişletir ${varname}. Ortam değişkenleri tarafından parametrelendirilen birkaç şeyle birkaç farklı sunucuda aynı .conf dosyalarını kullanmayı uygun buluyorum. / Opt / lampp / xampp (apache'yi başlatır) bu tür bir değerlendirmeyi sistem çevresinde pok exportyapan ve .conf dosyaları için değişkenleri tanımlamak için bash deyimleri çıktısı yapmak üzere düzenledim.
sootsnoot

@Joakim Alternatif, etkilenen .conf dosyalarının her birini, aynı alay üzerine dayalı olarak bir şablondan oluşturmak için bir komut dosyasına sahip olmak olacaktır. Yolum hakkında daha iyi sevdiğim bir şey, / opt / lampp / xampp'den geçmeden apache'yi başlatmanın eski çıktı komut dosyalarını kullanmaması, ancak ortam değişkenlerinin hiçbir şeye genişlememesi ve geçersiz direktifler yaratması nedeniyle başlamamasıdır.
sootsnoot

@Anthony Sottile Cevabı, $ (script-veya-program) civarında tırnak eklemek için düzenlediğinizi görüyorum. Lütfen bir örnek verebilir misiniz - foo.sh stdout'unda noktalı virgülle ayrılmış komutlarla aşağıdakiler iyi çalışır: echo '#! / Bin / bash'> foo.sh; echo 'echo "echo -na; echo -nb; echo -n c"' >> foo.sh; chmod 755 foo.sh; eval $ (./ foo.sh). Bu stdout üzerinde abc üretir. Çalışan ./foo.sh üretir: echo -na; echo -nb; echo -nc
sootsnoot

1
Eval kullanan ortak bir araç örneği için bkz. Pyenv . pyenv, Python'un birden fazla sürümü arasında kolayca geçiş yapmanızı sağlar. Sen koymak eval "$(pyenv init -)"senin içine .bash_profile(veya benzeri) kabuk yapılandırma dosyasında. Bu, küçük bir kabuk betiği oluşturur ve geçerli kabukta değerlendirir.
Jerry101

10

Eval ifadesi kabuğa eval'in argümanlarını komut olarak almasını ve komut satırı üzerinden çalıştırmasını söyler. Aşağıdaki gibi durumlarda yararlıdır:

Komut dosyanızda bir değişkene bir komut tanımlıyorsanız ve daha sonra bu komutu kullanmak istiyorsanız eval kullanmalısınız:

/home/user1 > a="ls | more"
/home/user1 > $a
bash: command not found: ls | more
/home/user1 > # Above command didn't work as ls tried to list file with name pipe (|) and more. But these files are not there
/home/user1 > eval $a
file.txt
mailids
remote_cmd.sh
sample.txt
tmp
/home/user1 >

4

Güncelleme: Bazı insanlar değerlendirmeyi asla kullanmamalı derler. Katılmıyorum. Bence risk, bozuk girdilere geçilebildiğinde ortaya çıkar eval. Bununla birlikte, bunun bir risk olmadığı birçok yaygın durum vardır ve bu nedenle her durumda eval'ün nasıl kullanılacağını bilmeye değer. Bu yığın akışı cevabı , değerlendirmenin risklerini ve değerlendirmeye alternatifleri açıklar. Sonuç olarak, eval'ün güvenli ve etkili olup olmadığını / ne zaman kullanılacağını belirlemek kullanıcıya bağlıdır.


Bash evaldeyimi, bash betiğiniz tarafından hesaplanan veya alınan kod satırlarını yürütmenizi sağlar.

Belki de en açık örnek, başka bir bash betiğini metin dosyası olarak açan, her metin satırını okuyan ve evalbunları sırayla yürütmek için kullanılan bir bash programı olabilir . Bu source, içe aktarılan komut dosyasının içeriğinde bir tür dönüşüm (örn. Filtreleme veya ikame) gerçekleştirmek gerekmedikçe , bash ifadesiyle aynı davranıştır .

Nadiren ihtiyacım vardı eval, ancak adları diğer değişkenlere atanan dizelerde bulunan değişkenleri okumak veya yazmak için yararlı buldum . Örneğin, kod ayak izini küçük tutarken ve fazlalıktan kaçınırken, değişken kümeleri üzerinde eylemler gerçekleştirmek için.

evalkavramsal olarak basittir. Bununla birlikte, bash dilinin sıkı sözdizimi ve bash tercümanının ayrıştırma sırası nüanslanabilir ve evalşifreli ve kullanımı veya anlaşılması zor görünebilir. İşte temel bilgiler:

  1. Aktarılan bağımsız değişken , çalışma zamanında hesaplanan evalbir dize ifadesidir . evalargümanının nihai çözümlü sonucunu , kodunuzda gerçek bir kod satırı olarak yürütür .

  2. Sözdizimi ve ayrıştırma sırası katıdır. Sonuç, yürütülebilir bir bash kodu satırı değilse, komut dosyanızın kapsamında, program, evalçöp işlemini yürütmeye çalışırken ifadede kilitlenir .

  3. Test sırasında evalifadeyi değiştirebilir echove neyin görüntülendiğine bakabilirsiniz. Geçerli bağlamda meşru bir kod ise, onu çalıştırmak evalişe yarayacaktır.


Aşağıdaki örnekler, değerlendirmenin nasıl çalıştığını netleştirmeye yardımcı olabilir ...

Örnek 1:

eval 'normal' kodun önündeki ifade bir NOP

$ eval a=b
$ eval echo $a
b

Yukarıdaki örnekte, ilk evalifadelerin bir amacı yoktur ve ortadan kaldırılabilir. evalilk satırda anlamsızdır, çünkü kodun dinamik bir yönü yoktur, yani zaten bash kodunun son satırlarına ayrıştırılmıştır, bu nedenle bash komut dosyasında normal bir kod ifadesi ile aynı olacaktır. 2 evaldönüştürme bir ayrıştırma adımı olmasına rağmen, çünkü çok anlamsız $aonun değişmez dize eşdeğer, hiçbir indirection yoktur (örneğin bir dize değeri yoluyla hiçbir referans gerçek bash isim veya bash-bekletilen komut değişkeni), bu aynı şekilde davranması diye evalöneki olmayan bir kod satırı olarak .



Örnek 2:

Dize değerleri olarak iletilen var adlarını kullanarak var ataması gerçekleştirin.

$ key="mykey"
$ val="myval"
$ eval $key=$val
$ echo $mykey
myval

Eğer olsaydınız echo $key=$val, çıktı şu olurdu:

mykey=myval

Yani , string ayrıştırma nihai sonucu olan, eval tarafından sonunda yankı ifadesinin dolayısıyla sonucunu çalıştırılacaktır budur ...



Örnek 3:

Örnek 2'ye daha fazla dolaylı ekleme

$ keyA="keyB"
$ valA="valB"
$ keyB="that"
$ valB="amazing"
$ eval eval \$$keyA=\$$valA
$ echo $that
amazing

Yukarıdakiler, önceki örneğe göre biraz daha karmaşıktır, bash'ın ayrıştırma sırasına ve özelliklerine daha fazla dayanmaktadır. evalÇizgi kabaca aşağıdaki sıraya göre içten çözümlenir alacağı (Aşağıdaki ifadeler sadece deyim nihai sonuca varmak için dahili olarak aşamalara ayrılmış olacaktı göstermek için denemek için, yalancı kod değil, gerçek kod unutmayın) .

 eval eval \$$keyA=\$$valA  # substitution of $keyA and $valA by interpreter
 eval eval \$keyB=\$valB    # convert '$' + name-strings to real vars by eval
 eval $keyB=$valB           # substitution of $keyB and $valB by interpreter
 eval that=amazing          # execute string literal 'that=amazing' by eval

Varsayılan ayrıştırma sırası, değerlendirmenin ne yaptığını açıklamıyorsa, üçüncü örnek neler olup bittiğini açıklığa kavuşturmak için ayrıştırmayı daha ayrıntılı olarak açıklayabilir.



Örnek 4:

Adları dizelerde bulunan değişkenlerin kendilerinin dize değerleri içerip içermediğini keşfedin .

a="User-provided"
b="Another user-provided optional value"
c=""

myvarname_a="a"
myvarname_b="b"
myvarname_c="c"

for varname in "myvarname_a" "myvarname_b" "myvarname_c"; do
    eval varval=\$$varname
    if [ -z "$varval" ]; then
        read -p "$varname? " $varname
    fi
done

İlk yinelemede:

varname="myvarname_a"

Bash, argümanı ayrıştırır evalve evaltam zamanında bunu şu anda görür:

eval varval=\$$myvarname_a

Aşağıdaki sözde kod, bash'ın gerçek kodun yukarıdaki satırını, tarafından yürütülen son değere ulaşmak için nasıl yorumladığını göstermeye çalışır . (aşağıdaki satırlar tam bash kodu değil, açıklayıcıdır):eval

1. eval varval="\$" + "$varname"      # This substitution resolved in eval statement
2. .................. "$myvarname_a"  # $myvarname_a previously resolved by for-loop
3. .................. "a"             # ... to this value
4. eval "varval=$a"                   # This requires one more parsing step
5. eval varval="User-provided"        # Final result of parsing (eval executes this)

Tüm ayrıştırma tamamlandığında, sonuç yürütülen şeydir ve etkisi açıktır, bu da evalkendisi hakkında özellikle gizemli bir şey olmadığını gösterir ve karmaşıklık argümanının ayrıştırılmasındadır .

varval="User-provided"

Yukarıdaki örnekte kalan kod, $ varval'a atanan değerin boş olup olmadığını sınamak için yeterlidir ve öyleyse kullanıcıdan bir değer vermesini ister.


3

Başlangıçta kasten eval kullanmayı öğrenmedim, çünkü çoğu insan veba gibi uzak durmayı önerecektir. Ancak yakın zamanda beni daha erken tanımadığım için facepalm yapan bir kullanım durumu keşfettim.

Test etmek için etkileşimli olarak çalıştırmak istediğiniz cron işleriniz varsa, dosyanın içeriğini cat ile görüntüleyebilir ve çalıştırmak için cron işini kopyalayıp yapıştırabilirsiniz. Ne yazık ki, bu benim kitabımda bir günah olan fareye dokunmayı içerir.

Diyelim ki /etc/cron.d/repeatme adresinde bir cron işiniz var:

*/10 * * * * root program arg1 arg2

Bunu önündeki tüm önemsiz metinlerle bir komut dosyası olarak çalıştıramazsınız, ancak tüm önemsiz şeylerden kurtulmak, bir alt kabuğa sarmak ve dizeyi eval ile yürütmek için cut'u kullanabiliriz.

eval $( cut -d ' ' -f 6- /etc/cron.d/repeatme)

Kes komutu, dosyanın boşluklarla ayrılmış 6. alanını yazdırır. Eval sonra bu komutu yürütür.

Burada örnek olarak bir cron işi kullandım, ancak kavram stdout'tan metni biçimlendirmek ve daha sonra bu metni değerlendirmektir.

Bu durumda eval kullanımı güvenli değildir, çünkü önceden ne değerlendireceğimizi tam olarak biliyoruz.


2

Son zamanlarda evalihtiyacım olan sırayla birden fazla küme ayracı genişletme zorlamak için kullanmak zorunda kaldı . Bash soldan sağa birden çok ayraç genişletmesi yapar,

xargs -I_ cat _/{11..15}/{8..5}.jpg

genişler

xargs -I_ cat _/11/8.jpg _/11/7.jpg _/11/6.jpg _/11/5.jpg _/12/8.jpg _/12/7.jpg _/12/6.jpg _/12/5.jpg _/13/8.jpg _/13/7.jpg _/13/6.jpg _/13/5.jpg _/14/8.jpg _/14/7.jpg _/14/6.jpg _/14/5.jpg _/15/8.jpg _/15/7.jpg _/15/6.jpg _/15/5.jpg

ama önce ikinci brace genişlemesine ihtiyacım vardı,

xargs -I_ cat _/11/8.jpg _/12/8.jpg _/13/8.jpg _/14/8.jpg _/15/8.jpg _/11/7.jpg _/12/7.jpg _/13/7.jpg _/14/7.jpg _/15/7.jpg _/11/6.jpg _/12/6.jpg _/13/6.jpg _/14/6.jpg _/15/6.jpg _/11/5.jpg _/12/5.jpg _/13/5.jpg _/14/5.jpg _/15/5.jpg

Bunu yapabileceğim en iyi şey

xargs -I_ cat $(eval echo _/'{11..15}'/{8..5}.jpg)

Çünkü tek tırnaklar ilk parantez kümesini evalkomut satırının ayrıştırılması sırasında genişlemeden koruyarak, çağrılan alt kabuk tarafından genişletilmesini sağlar eval.

Bunun bir adımda gerçekleşmesine izin veren iç içe geçmiş ayraç genişletmelerini içeren bazı kurnaz şema olabilir, ancak varsa çok görmek için aptalım.


1

Tipik kullanımları sordunuz.

Kabuk komut dosyası oluşturma ile ilgili yaygın bir şikayet, değerleri işlevlerden geri almak için (iddia edildiği gibi) referansla geçemeyeceğinizdir.

Ama aslında, "eval" üzerinden, sen yapabilirsiniz referans olarak geçmektedir. Arayan, arayan tarafından değerlendirilecek değişken atamalarının bir listesini geri gönderebilir. Referans tarafından geçilir, çünkü arayanın sonuç değişken (ler) inin ad (lar) ını belirtmesine izin verilebilir - aşağıdaki örneğe bakınız. Hata sonuçları errno ve errstr gibi standart isimlerden geri aktarılabilir.

İşte bash'ta referans olarak geçme örneği:

#!/bin/bash
isint()
{
    re='^[-]?[0-9]+$'
    [[ $1 =~ $re ]]
}

#args 1: name of result variable, 2: first addend, 3: second addend 
iadd()
{
    if isint ${2} && isint ${3} ; then
        echo "$1=$((${2}+${3}));errno=0"
        return 0
    else
        echo "errstr=\"Error: non-integer argument to iadd $*\" ; errno=329"
        return 1
    fi
}

var=1
echo "[1] var=$var"

eval $(iadd var A B)
if [[ $errno -ne 0 ]]; then
    echo "errstr=$errstr"
    echo "errno=$errno"
fi
echo "[2] var=$var (unchanged after error)"

eval $(iadd var $var 1)
if [[ $errno -ne 0 ]]; then
    echo "errstr=$errstr"
    echo "errno=$errno"
fi  
echo "[3] var=$var (successfully changed)"

Çıktı şöyle görünür:

[1] var=1
errstr=Error: non-integer argument to iadd var A B
errno=329
[2] var=1 (unchanged after error)
[3] var=2 (successfully changed)

Bu metin çıktısında neredeyse sınırsız bant genişliği var! Birden fazla çıktı satırı kullanılırsa daha fazla olasılık vardır: örneğin, ilk satır değişken atamalar için kullanılabilir, ikincisi sürekli 'düşünce akışı' için kullanılabilir, ancak bu bu yazının kapsamı dışındadır.


"daha fazla olasılık" olduğunu söylemek önemsiz, önemsiz ve az söylemek gereksizdir.
dotbit

0

"İfadenizi yürütmeden önce bir kez daha değerlendirme" yanıtını beğeniyorum ve başka bir örnekle açıklığa kavuşturmak istiyorum.

var="\"par1 par2\""
echo $var # prints nicely "par1 par2"

function cntpars() {
  echo "  > Count: $#"
  echo "  > Pars : $*"
  echo "  > par1 : $1"
  echo "  > par2 : $2"

  if [[ $# = 1 && $1 = "par1 par2" ]]; then
    echo "  > PASS"
  else
    echo "  > FAIL"
    return 1
  fi
}

# Option 1: Will Pass
echo "eval \"cntpars \$var\""
eval "cntpars $var"

# Option 2: Will Fail, with curious results
echo "cntpars \$var"
cntpars $var

Seçenek 2'deki meraklı sonuçlar, 2 parametreyi aşağıdaki gibi geçireceğimizdir:

  • İlk Parametre: "value
  • İkinci Parametre: content"

Bu karşı sezgisel için nasıl? Ek eval, bunu düzeltir.

Https://stackoverflow.com/a/40646371/744133 adresinden uyarlanmıştır.


0

Soruda:

who | grep $(tty | sed s:/dev/::)

a ve tty dosyalarının olmadığını iddia eden hatalar verir. Bunu tty'nin grep'in yürütülmesinden önce yorumlanmadığı, bunun yerine bash'ın tty'yi grep'e bir parametre olarak geçirdiğini ve bunun bir dosya adı olarak yorumladığını anladım.

Ayrıca, alt işlemi belirtmesi gereken eşleşen parantezler tarafından ele alınması gereken iç içe yönlendirme durumu da vardır, ancak bash ilk olarak bir programa gönderilecek parametreler oluşturan bir sözcük ayırıcıdır, bu nedenle parantezler önce eşleştirilmez, ancak olarak yorumlanır görüldü.

Ben grep ile belirli var ve bir boru yerine dosyayı bir parametre olarak belirtildi. Ayrıca, bir komuttan çıktıyı dosya olarak geçirerek temel komutu basitleştirdim, böylece g / ç boruları yuvalanmayacaktı:

grep $(tty | sed s:/dev/::) <(who)

iyi çalışıyor.

who | grep $(echo pts/3)

gerçekten arzu edilmez, ancak iç içe boruyu ortadan kaldırır ve aynı zamanda iyi çalışır.

Sonuç olarak, bash iç içe geçmiş pipping gibi görünmüyor. Bash'in özyinelemeli bir şekilde yazılmış yeni bir dalga programı olmadığını anlamak önemlidir. Bunun yerine, bash, özelliklere eklenmiş eski bir 1,2,3 programıdır. Geriye dönük uyumluluğu sağlamak amacıyla, ilk yorumlama şekli hiçbir zaman değiştirilmemiştir. Eğer bash ilk parantezleri eşleştirmek için yeniden yazıldıysa, kaç bash programına kaç hata sokulur? Birçok programcı şifreli olmayı sever.

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.