Bash içinde $ (komut yerine koyma) içinde alıntı yapmak


126

Bash ortamımda boşluk içeren değişkenler kullanıyorum ve bu değişkenleri komut değiştirme içinde kullanıyorum. Maalesef SE'de cevabı bulamıyorum.

Değişkenlerimi teklif etmenin doğru yolu nedir? Bunlar iç içe geçmişse nasıl yapmalıyım?

DIRNAME=$(dirname "$FILE")

ya da ikamenin dışında teklif verebilir miyim?

DIRNAME="$(dirname $FILE)"

ya da her ikisi de?

DIRNAME="$(dirname "$FILE")"

veya geri keneler kullanıyor muyum?

DIRNAME=`dirname "$FILE"`

Bunu yapmanın doğru yolu nedir? Tekliflerin doğru ayarlanıp ayarlanmadığını kolayca nasıl kontrol edebilirim?




1
Bu iyi bir soru, ancak gömülü boşlukları olan tüm meseleler göz önüne alındığında, neden bunları bilerek kullanarak hayatı zorlaştırıyorsunuz?
Joe

3
@Joe, gömülü boşluklarla dosya adlarında boşluk mu kastediyorsunuz? Şahsen onları bu kadar sık ​​kullanmıyorum, ancak boşlukları varsa emin olamadığım diğer halkların dizinleri ve dosyaları ile çalışıyorum. Ayrıca, bir kerede doğru yapmanın daha iyi olacağını düşünüyorum, bu yüzden gelecekte endişelenmenize gerek yok.
Kuzen Kokain,

4
Evet. Bu "diğer insanlarla" ne yapacağız? <G>
Joe

Yanıtlar:


188

En kötüsünden en iyisine sırayla:

  • DIRNAME="$(dirname $FILE)" Ne istediğinizi yapmayacağım eğer $FILEboşluk veya globbing karakterler içeriyor \[?*.
  • DIRNAME=`dirname "$FILE"`teknik olarak doğru, ancak iç içe geçme sırasında ortaya çıkan ekstra karmaşıklık nedeniyle komut genişletme işlemleri için geri tepiciler önerilmez .
  • DIRNAME=$(dirname "$FILE")doğrudur, ancak yalnızca bu bir atama olduğu için . Aşağıdaki gibi herhangi bir başka bağlamda, komut ikamesi kullanırsanız export DIRNAME=$(dirname "$FILE")veya du $(dirname "$FILE")genişlemesi sonucu boşluk veya globbing karakterleri içeriyorsa, tırnak eksikliği sorun neden olur.
  • DIRNAME="$(dirname "$FILE")"önerilen yoldur. Sen yerini alabilecek DIRNAME=bir komuta ve başka bir şey değiştirmeden bir boşluk ve dirnamedoğru dizeyi alır.

Daha da geliştirmek için:

  • DIRNAME="$(dirname -- "$FILE")"$FILEbir çizgi ile başlarsa çalışır .
  • DIRNAME="$(dirname -- "$FILE"; printf x)" && DIRNAME="${DIRNAME%?x}"Yeni $FILEbir satırla bitse bile çalışır , çünkü $()çıktı sonunda yeni satırları keser dirnameve sonuçtan sonra yeni satır çıkarır. Sheesh dirname, neden farklı olmalısın?

Komut açılımlarını istediğiniz kadar yuvalayabilirsiniz. İle $()size her zaman yeni alıntı içeriğini oluşturmak, böylece bu gibi şeyler yapabilirsiniz:

foo "$(bar "$(baz "$(ban "bla")")")"

Sen do not ters tırnakların o denemek istiyorum.


3
Soruma açık cevap. Bu değişkenleri yerleştirdiğimde, şimdi yaptığım gibi alıntı yapmaya devam edebilir miyim?
Kuzen Kokain

3
Alıntılar içinde bir komut alt bölümündeki tırnakların davranışını ayrıntılandıran bir referans / kaynak var mı?
AmadeusDrZaius

12
@AmadeusDrZaius " $()Her zaman yeni bir alıntı bağlamı yaratırsınız" , bu yüzden dış alıntıların dışında gibi. Bildiğim kadarıyla, başka hiçbir şey yok.
l0b0

4
@ l0b0 Teşekkürler, evet, açıklamanızı çok net buldum. Sadece bir yerde bir el kitabında olup olmadığını merak ediyordum. Bunu yünlüde (gayri resmi olarak da olsa) buldum . Sanırım ikame sırasını dikkatlice okursanız , bunun bir sonucu olabilir.
AmadeusDrZaius

1
Bu yüzden iç içe alıntılar kabul edilebilir, ancak çoğu sözdizimi renklendirme şemasının özel durumu algılayamadığı için bizi atıyorlar. Temiz.
Luke Davis

19

Değişken alıntılamanın etkilerini her zaman ile gösterebilirsiniz printf.

Kelime bölme işlemi yapıldı var1:

$ var1="hello     world"
$ printf '[%s]\n' $var1
[hello]
[world]

var1 alıntı, yani hiçbir kelime bölme:

$ printf '[%s]\n' "$var1"
[hello     world]

var1İçinde sözcük bölme $(), eşdeğer echo "hello" "world":

$ var2=$(echo $var1)
$ printf '[%s]\n' "$var2"
[hello world]

Hiçbir kelime bölme, var1alıntı yapma ile ilgili bir sorun yok $():

$ var2=$(echo "$var1")
$ printf '[%s]\n' "$var2"
[hello     world]

Word var1tekrar bölünüyor :

$ var2="$(echo $var1)"
$ printf '[%s]\n' "$var2"
[hello world]

İkisinden de alıntı yapmak, emin olmanın en kolay yolu.

$ var2="$(echo "$var1")"
$ printf '[%s]\n' "$var2"
[hello     world]

Globbing sorunu

Bir değişkeni alıntılamamak aynı zamanda içeriğinin glob genişlemesine yol açabilir:

$ mkdir test; cd test; touch file1 file2
$ var="*"
$ printf '[%s]\n' $var
[file1]
[file2]
$ printf '[%s]\n' "$var"
[*]

Bunun, değişken yalnızca genişletildikten sonra gerçekleştiğini unutmayın. Görevlendirme sırasında bir teklif vermek gerekli değildir:

$ var=*
$ printf '[%s]\n' $var
[file1]
[file2]
$ printf '[%s]\n' "$var"
[*]

set -fBu davranışı devre dışı bırakmak için kullanın :

$ set -f
$ var=*
$ printf '[%s]\n' $var
[*]

Ve set +fyeniden etkinleştirmek için:

$ set +f
$ printf '[%s]\n' $var
[file1]
[file2]

8
İnsanlar kelimelerin bölünmesinin tek sorun olmadığını unutmaya meyillidir, örneğin var1='hello * world'küreselleşme sorununu da göstermek için örneğinizi değiştirmek isteyebilirsiniz .
Stéphane Chazelas

10

Kabul edilen cevabın eklenmesi:

@ L0b0'ın buradaki cevabına genel olarak katılıyorum, ancak "back to best" listesinde çıplak backtick'lerin yerleştirilmesinin en azından kısmen $(...)her yerde mevcut olan varsayımın bir sonucu olduğunu düşünüyorum . Sorunun Bash'i belirttiğinin farkındayım, ancak Bash'in ne zaman ortaya çıktığı, çoğu /bin/shzaman tam olarak Bourne Again'nin tam kabuğu olmayabilir.

Özellikle, düz Bourne kabuğu ne yapılacağını bilmeyecektir $(...), bu nedenle kendisiyle uyumlu olduğunu iddia eden komut dosyaları (örneğin, bir #!/bin/shshebang çizgisi aracılığıyla ) aslında "gerçek" tarafından çalıştırıldıysa muhtemelen yaramazlık yapacaktır /bin/sh- bu örneğin, başlangıç ​​senaryoları üretilirken veya senaryo öncesi ve sonrası ambalajlanırken özel ilgi, baz bir sistemin kurulumu sırasında şaşırtıcı bir yere inebilir.

Bunlardan herhangi biri bu değişkenle yapmayı planladığınız bir şeye benziyorsa, yuvalama muhtemelen senaryoyu gerçekten tahmin edilebilecek şekilde çalıştırmaktan daha az endişe vericidir. Yeterince basit bir durum olduğunda ve taşınabilirlik bir endişe kaynağı olsa da, senaryonun genellikle/bin/sh Bash'in bulunduğu sistemlerde çalışmasını beklesem de, genellikle bu nedenle yuvalama yerine çoklu atamalarla backticks kullanma eğilimindeyim.

Bunların hepsini uyguladıktan sonra, uygulayan mermilerin ubiquity $(...)(Bash, Dash, vd.) Bizi çoğu zaman daha güzel, yuvalanması daha kolay ve daha yakın zamanda tercih edilen POSIX sözdizimine bağlı kalmak için iyi bir noktada bırakır. @ l0b0 tüm nedenlerden bahseder.

Bir kenara: bu arada StackOverflow'ta da ortaya çıktı -


//, Mükemmel cevap. Ben de geçmişte / bin / sh ile uyumluluk sorunları yaşadım. Geriye dönük uyumlu yöntemler kullanarak problemiyle nasıl başa çıkılacağı konusunda herhangi bir tavsiyeniz var mı?
Nathan Basanese,

Tecrübelerime göre: Bazen backtick'leri kapalı dolar para birimine dönüştürmeniz gerekebilir ve bir zamanlar backtick'lerin içine dolanmış dolar parasını değiştirmek için hiçbir zaman yardımcı olmadınız (özel olması gerekiyordu). Ben sadece javascript kodumda ters kabuk yazarım, kabuk kodunda yazmam.
Steven Lu
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.