Kabuk betiklerinde backticks yerine $ () kullanmanın faydası nedir?


175

Komut satırı çıktısını yakalamanın iki yolu vardır bash:

  1. Eski Bourne kabuğu backticks ``:

    var=`command`
  2. $() sözdizimi (bildiğim kadarıyla Bash'a özgüdür veya en azından orijinal Bourne gibi POSIX olmayan eski kabuklar tarafından desteklenmez)

    var=$(command)

İkinci sözdiziminin backticks ile karşılaştırılmasının herhangi bir faydası var mı? Yoksa ikisi tamamen% 100 eşdeğeri mi?


15
$()POSIX ve ksh, bash, ash, dash, zsh, busybox gibi tüm modern Bourne kabukları tarafından destekleniyor. (Çok modern olmayan bir tanesi Solaris'tir /bin/sh, ancak Solaris'te moderni kullandığınızdan emin olursunuz /usr/xpg4/bin/sh).
Jens

27
Ayrıca, $()takma adlarda kullanma ve geri çekme ile ilgili bir not . Eğer varsa alias foo=$(command)sizin içinde .bashrco commandtakma komut zaman çalıştırılacaktır kendisi esnasında çalıştırılır .bashrcyorumlanması. İle alias foo=`command`, commandtakma ad her çalıştırıldığında yürütülür. Kaçmak Ama eğer $birlikte $()formu (örn alias foo=\$(command)), çok yerine esnasında, takma her çalıştırıldığında çalıştırır .bashrcyorumlanması. Zaten test ederek söyleyebildiğim kadarıyla; Bash belgelerinde bu davranışı açıklayan hiçbir şey bulamıyorum.
dirtside

4
@dirtside Bu kabuk, bash ve POSIX kabuğunu test ettim, kaynak yaptığımda backtick çalıştırılıyor. Basit örnek: alias curDate = `date` Bunu kaynakladıktan ve curDate'i çalıştırdıktan sonra, örneğin Mon (Pazartesi günü Kaynaklı) komutunu bulamadığını belirten bir mesaj alıyorum.
thecarpy

@dirtside Doğru değil. Takma adla bile foo = `command` commandsadece bir kez yürütülür. Kontrol ettim: işlev aaa () {printf tarih; echo aaa >> ~ / test.txt; takma ad testi1 = aaa. Aaa işlevi, kaç kez takma ad ( test1) yürütüldüğüne bakılmaksızın yalnızca bir kez (her girişten sonra) yürütmektedir . .Bashrc (Debian 10'da) kullandım.
vitaliydev

Bilmiyorum, beş buçuk yıl önce bash'ı kullandığım her neyse, muhtemelen bu noktada bile oldukça eskiydi. Testlerimin yanlış olması olası, ancak şu an pek zor değil çünkü kimsenin hala hangi sürümü kullandığından şüpheliyim.
dirtside

Yanıtlar:


156

Bunlardan en önemlisi, akıl sağlığınızı kaybetmeden, komutların içindeki komutları, iç içe geçebilme yeteneğidir .

Bir örnek, biraz da olsa:

deps=$(find /dir -name $(ls -1tr 201112[0-9][0-9]*.txt | tail -1l) -print)

size tüm dosyaların listesini verir. /dirdizin ağacındaki Aralık 2011'den önceki en eski tarihli metin dosyasıyla aynı ada sahip (a) .

Başka bir örnek, üst dizinin adını (tam yol değil) almak gibi bir şey olabilir:

pax> cd /home/pax/xyzzy/plugh
pax> parent=$(basename $(dirname $PWD))
pax> echo $parent
xyzzy

(a) Şimdi belirli bir komut aslında çalışmayabilir, işlevselliği test etmedim. Bu yüzden, bana bunun için oy verirseniz, niyetin görüşünü kaybettiniz :-) Bu, hataya hazır bir üretime hazır snippet olarak değil, nasıl yuva yapabileceğinizin bir örneği olarak kastedilmiştir.


87
SO'daki tüm kodların NASA servis kodu güvenilirlik standartlarına göre tasarlanmış üretime hazır snippet'ler olmasını bekliyorum. Daha az bir şey bir bayrak ve silme oyu alır.
DVK

1
@DVK Şaka yapmamanız durumunda, bu tür garantilere veya amaca uygunluğa izin vermek için kod katkılarının SO varsayılanlarından (CC-BY-SA veya MIT lisansları) otomatik olarak uzaklaşamadıkları için işaretlenmesi gerektiğini kabul etmiyorum. Ben vs. yardımseverlik, Teknik değerin göre yerine kendi risk altında SO kod yeniden kullanımı olur ve oy katkıları
chrstphrchvz

9
@chrstphrchvz, profilime bakarsanız şu küçük snippet'i görürsünüz: "Stack Overflow'da yayınladığım tüm kod, tam metni şudur: Do whatever the heck you want with it." Onunla ne istersen yap "lisansı kapsamındadır: :-) Her durumda, DVK'dan mizah olduğuna eminim.
paxdiablo

1
ama "cd / home / pax / xyzzy / plover" olsaydı? Kendinizi farklı küçük bükümlü pasajlardan oluşan bir labirentte bulabilir misiniz?
Duncan

@wchargin, bu olayın C ++ diline kullanılmış tanımlanmış değişmez değerlerin eklenmesi için birincil motivasyon kaynağı olduğunu biliyor muydunuz? en.cppreference.com/w/cpp/language/user_literal
ThomasMcLeod

59

gccYüklü olan yere karşılık gelen lib dizinini bulmak istediğinizi varsayalım . Bir seçeneğin var:

libdir=$(dirname $(dirname $(which gcc)))/lib

libdir=`dirname \`dirname \\\`which gcc\\\`\``/lib

Birincisi ikincisinden daha kolaydır - ilkini kullanın.


4
Bu komuta ikameleri hakkında bazı alıntılar görmek iyi olurdu!
Tom Fenech

1
En azından bash için @TomFenech'in yorumu ödevler için geçerli değildir. x=$(f); x=`f` gibi davranmak x="$(f)"; x="`f`". Buna karşılık, dizi atama x=($(f)); x=(`f`) yapmak yarmaya gerçekleştirmek $IFSkomutlar çağrılırken beklendiği gibi karakterler. Bu uygundur ( x=1 2 3 4mantıklı değil) ama tutarsız.
kdb

@kdb x=$(f)tırnak işaretleri olmadan çalışma konusunda haklısın . Daha spesifik olmalıydım; Kullanmayı teklif ediyordum libdir=$(dirname "$(dirname "$(which gcc)")")/lib( komuta ikameleriyle ilgili alıntılar ). Belirtilmemiş olarak bırakılırsa, normal sözcük bölme ve glob genişletme işlemine tabi olursunuz.
Tom Fenech

38

Backticks ( `...`), yalnızca POSIX uyumlu olmayan bourne mermilerinin en eskisinin gerektirdiği eski sözdizimidir ve $(...)POSIX ve birkaç nedenden dolayı daha çok tercih edilir:

  • Ters \çentiklerin içindeki ters eğik çizgiler ( ) açık olmayan bir şekilde ele alınır:

    $ echo "`echo \\a`" "$(echo \\a)"
    a \a
    $ echo "`echo \\\\a`" "$(echo \\\\a)"
    \a \\a
    # Note that this is true for *single quotes* too!
    $ foo=`echo '\\'`; bar=$(echo '\\'); echo "foo is $foo, bar is $bar" 
    foo is \, bar is \\
  • İçerideki alıntılar $()çok daha uygundur:

    echo "x is $(sed ... <<<"$y")"

    onun yerine:

    echo "x is `sed ... <<<\"$y\"`"

    veya şöyle bir şey yazmak:

    IPs_inna_string=`awk "/\`cat /etc/myname\`/"'{print $1}' /etc/hosts`

    çünkü $()alıntı yapmak için tamamen yeni bir bağlam kullanıyor

    Bourne ve Korn mermileri bu ters eğik çizgileri gerektireceği için taşınabilir değildir, Bash ve dash gerektirmez.

  • Yerleştirme komutu ikamelerinin sözdizimi daha kolaydır:

    x=$(grep "$(dirname "$path")" file)

    daha:

    x=`grep "\`dirname \"$path\"\`" file`

    Çünkü $() alıntı için tamamen yeni bir bağlam uygular, bu nedenle her komut ikamesi korunur ve alıntı ve kaçış konusunda özel bir endişe duyulmadan kendi başına tedavi edilebilir. Geri vuruşları kullanırken, iki ve daha üst seviyelerden sonra daha çirkin ve daha çirkinleşir.

    Birkaç örnek daha:

    echo `echo `ls``      # INCORRECT
    echo `echo \`ls\``    # CORRECT
    echo $(echo $(ls))    # CORRECT
  • Geri tırnak işaretlerini kullanırken tutarsız bir davranış sorununu çözer:

    • echo '\$x' çıktılar \$x
    • echo `echo '\$x'` çıktılar $x
    • echo $(echo '\$x') çıktılar \$x
  • Backticks sözdizimi, katıştırılmış komutun içeriğinde geçmiş kısıtlamalara sahiptir ve geri tırnak içeren bazı geçerli komut dosyalarını işleyemezken, yeni $()form her türlü geçerli katıştırılmış komut dosyasını işleyebilir.

    Örneğin, aksi takdirde geçerli olan bu gömülü komut dosyaları sol sütunda çalışmaz, ancak sağ IEEE üzerinde çalışır :

    echo `                         echo $(
    cat <<\eof                     cat <<\eof
    a here-doc with `              a here-doc with )
    eof                            eof
    `                              )
    
    
    echo `                         echo $(
    echo abc # a comment with `    echo abc # a comment with )
    `                              )
    
    
    echo `                         echo $(
    echo '`'                       echo ')'
    `                              )

Bu nedenle $-prefixed komut ikamesi için sözdizimi tercih edilen yöntem olmalıdır, çünkü temiz sözdizimi ile görsel olarak açıktır (insan ve makine okunabilirliğini artırır), seçilebilir ve sezgiseldir, iç ayrıştırma ayrıdır ve ayrıca daha tutarlıdır (ile çift ​​tırnak içinde ayrıştırılan diğer tüm genişletmeler), geri dönüşlerin tek istisna olduğu ve `karakterin ", özellikle küçük veya olağandışı yazı tipleriyle, okumayı daha da zorlaştıracak şekilde bitişik olduğunda kolayca kamufle edilebilir .

Kaynak: Neden $(...)tercih edilir `...`(backticks)? BashFAQ şirketinde

Ayrıca bakınız:


1
Aksanıyla alıntı Yuvalanmış gravis tarzı ikamesi aslında tanımsız, çift tırnak kullanabilirsiniz ya dışını veya içini değil portably, hem. Kabuklar onları farklı yorumlar; bazıları kaçmak için ters eğik çizgi ister, bazıları ters eğik çizgiden kaçmamasını gerektirir.
mirabilos

23

Man bash'tan:

          $(command)
   or
          `command`

   Bash performs the expansion by executing command and replacing the com-
   mand  substitution  with  the  standard output of the command, with any
   trailing newlines deleted.  Embedded newlines are not deleted, but they
   may  be  removed during word splitting.  The command substitution $(cat
   file) can be replaced by the equivalent but faster $(< file).

   When the old-style backquote form of substitution  is  used,  backslash
   retains  its  literal  meaning except when followed by $, `, or \.  The
   first backquote not preceded by a backslash terminates the command sub-
   stitution.   When using the $(command) form, all characters between the
   parentheses make up the command; none are treated specially.

9

Diğer cevaplara ek olarak,

$(...)

görsel olarak daha iyi göze çarpıyor

`...`

Backticks apostroflara çok benziyor; bu, kullandığınız yazı tipine bağlı olarak değişir.

(Ve daha önce fark ettiğim gibi, backticks satır içi kod örneklerine girmek çok daha zor.)


2
tuhaf bir klavyeniz olmalı (ya da var mı?). Benim için, geri tıklamak çok daha kolay - sol üst köşedeki anahtar, SHIFT veya ALT gerekli değil.
DVK

1
@DVK: Yazma kolaylığı değil, görünüşlerinden bahsediyordum. (My klavye muhtemelen sizinki ile aynı.) Yine de, şimdi sen söyleyince, ben daha iyi kas hafızası var $ (ve )ben backtick gerçekleştirdikleri işlevden; YMMV.
Keith Thompson

bash'da hiç programlanmadı (eski ksh'tan Perl'e atlandı), bu belirli sözdizimi için kesinlikle bellek yok :)
DVK

1
@DVK, Keith'in burada blok olmayan kodun (blok kodunun satırın başında dört boşluk kullanılması anlamına geldiğini) belirtmek için backticks kullandığını, onlara backticks koymayı zorlaştırdığını, yuvalama zorlukları :-) FWIW, kod ve / kod etiketlerini (blok olmayan kod yapmanın diğer yolu) daha kolay bir şekilde geri tıklamaları içerebileceğini görebilirsiniz.
paxdiablo

@Pax - anladım. Duh! Gerçekten bir sebepten dolayı zihinsel olarak blok kodlara takıldım.
DVK

7

$() yuvalanmaya izin verir.

out=$(echo today is $(date))

Bence backticks buna izin vermiyor.


2
İğneleri yuvalayabilirsiniz; çok daha zor olduğunu: out=`echo today is \`date\`` .
Jonathan Leffler

4

$(command)Komut yerine koyma biçimini tanımlayan POSIX standardıdır . Günümüzde kullanılan çoğu mermi POSIX uyumludur ve bu tercih edilen formu arkaik gergi notasyonu üzerinde destekler. Komut ikamesi Shell Dil belgenin bölüm (2.6.3) Bu açıklar:

Komut ikamesi, bir komutun çıktısının komut adının yerine ikame edilmesine izin verir. Komut ikamesi, komut aşağıdaki gibi kapatıldığında gerçekleşir:

$(command)

veya (geriye dönük sürüm):

`command`

Kabuk, bir alt kabuk ortamında komut yürüterek (bkz. Kabuk Yürütme Ortamı ) ve komut ikamesini ( komut metni artı "$ ()" veya geri tırnaklar ekleyerek) komutun standart çıktısı ile değiştirerek komut ikamesini genişletir. <newline>ikamenin sonunda bir veya daha fazla karakterin dizileri . <newline>Çıktı bitmeden gömülü karakterler kaldırılmamalıdır; bununla birlikte, bunlar IFS değerine ve yürürlükte olan alıntıya bağlı olarak alan ayırıcılar olarak ele alınabilir ve alan ayrılması sırasında elimine edilebilir. Çıktı herhangi bir boş bayt içeriyorsa, davranış belirtilmez.

Geri verilen komut yerine koyma tarzı içinde, <backslash>' $', ' `' veya izleyen durumlar dışında gerçek anlamını koruyacaktır <backslash>. Eşleşen backquote araştırması, alıntılanmamış ilk kaçan backquote tarafından sağlanacaktır; bu arama sırasında, bir kabuk yorumu, burada bir belge, $ ( komut ) formunun katıştırılmış komut ikamesi veya tırnak içine alınmış bir dize içinde kaçan bir geri sorgula karşılaşılırsa tanımsız sonuçlar oluşur. " `...`" Dizisi içinde başlayan, ancak bitmeyen tek tırnaklı veya çift tırnaklı bir dize , tanımlanmamış sonuçlar üretir.

$ ( Komut ) formuyla, eşleşen kapanış parantezine açık parantezin ardından gelen tüm karakterler komutu oluşturur . Herhangi geçerli kabuk komut dosyası için kullanılabilir komutu yalnızca belirtilmemiş sonuçlar üretir yönlendirmeler oluşan bir senaryo dışında.

Komut ikamesinin sonuçları, daha fazla tilde genişletme, parametre genişletme, komut ikame veya aritmetik genişletme için işlenmeyecektir. Çift tırnak içinde bir komut ikamesi meydana gelirse, ikame sonuçları üzerinde alan bölme ve yol adı genişletme yapılmayacaktır.

Komut ikamesi yuvalanabilir. Geri sıralanan sürümde iç içe yerleştirmeyi belirtmek için, uygulama iç arka tırnaklardan <backslash>karakterlerle önce gelir ; Örneğin:

\`command\`

Shell komut dilinin sözdizimi, "$((", alt kabukla başlayan bir aritmetik genişletme veya komut ikamesi ekleyebilir. Aritmetik genişletme önceliğe sahiptir; yani, kabuk ilk olarak genişletmeyi bir aritmetik genişletme olarak ayrışıp ayrıştıramayacağını belirler ve yalnızca genişletmeyi bir komut olarak ayrıştırır genişletmeyi aritmetik bir genişletme olarak ayrıştıramadığını belirlerse, bu belirleme yapılırken kabuğun iç içe açılımları değerlendirmesine gerek yoktur.Genişlemeyi bir aritmetik genişletme olarak ayrıştıramadığını zaten belirlemeden girişin sonuna rastlarsa, kabuk, genişletmeyi tamamlanmamış bir aritmetik genişletme olarak ele alır ve bir sözdizimi hatası bildirir. Uygun bir uygulama, "$( " ve "('alt kabuk ile başlayan bir komut yerine iki jeton (yani boşlukla ayırın). Örneğin, tek bir alt kabuk içeren bir komut ikamesi şöyle yazılabilir:

$( (command) )


4

Bu eski bir soru ama ben bir tamamen geçerli örnekle geldi $(...)over `...`.

Cygwin çalıştıran pencerelere uzak bir masaüstü kullanıyordum ve bir komutun sonucunu yinelemek istedim. Ne yazık ki, uzak masaüstü şeyi veya cygwin'in kendisi nedeniyle backtick karakterinin girilmesi imkansızdı.

Bir dolar işareti ve parantezin bu tür garip kurulumlarda yazılmasının daha kolay olacağını varsaymak mantıklıdır.

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.