Bash'deki çift tırnak işaretleri nasıl eşleştirilir (eşleştirilir)?


15

Kullanıyorum GNU bash 4.3.48. Yalnızca tek bir dolar işareti ile farklılık gösteren aşağıdaki iki komutu göz önünde bulundurun.

Komut 1:

echo "(echo " * ")"

Komut 2:

echo "$(echo " * ")"

Bunların çıktıları sırasıyla

(echo  test.txt ppcg.sh )

ve

 * 

Açık bir şekilde, birinci durumda *, globbed edilir, bu da ilk tırnak işaretinin ikincisini bir çift oluşturmak için gittiğini ve üçüncü ve dördüncüsünü başka bir çifti oluşturduğu anlamına gelir.

İkinci durumda, *globbed edilmez ve çıktıda, yıldız işaretinden önce ve sonra olmak üzere tam olarak iki ekstra boşluk vardır, bu da ikinci tırnak işaretinin üçüncü ile birlikte, dördüncüyle birlikte gittiği anlamına gelir.

$()Yapının yanı sıra, tırnak işaretlerinin bir sonrakiyle eşleşmediği, bunun yerine iç içe geçtiği başka durumlar var mı? Bu davranış iyi belgelenmiş midir ve evet ise ilgili belgeyi nerede bulabilirim?



Bir kez daha, UL SE'deki sözdizimi vurgusu yanlış ve yanıltıcıdır, ancak JS'yi suçlamak için güzeldir.
Weijun Zhou

Yanıtlar:


14

Dizelerin içine enterpole edilebilecek iç içe geçmiş yapıların içinde başka dizeler de olabilir: yeni bir komut dosyası gibi, kapanış işaretçisine kadar ayrıştırılırlar ve hatta birden çok düzey derinliğinde iç içe bile yerleştirilebilirler. Tüm çubuklardan biri a ile başlar $. Hepsi Bash manuel ve POSIX kabuk komut dili spesifikasyonunun bir kombinasyonu halinde belgelenmiştir.

Bu yapıların birkaç örneği vardır:

  • Bulduğunuz gibi ile ikame komutunu verin$( ... ) . POSIX bu davranışı belirtir :

    İle $(command)forma, eşleştirme kapanış parantez açık parantez aşağıdaki tüm karakterler komutunu oluşturmaktadır. Herhangi geçerli kabuk komut dosyası için kullanılabilir komutu ...

    Alıntılar geçerli kabuk komut dosyalarının bir parçasıdır, bu yüzden normal anlamlarına izin verilir.

  • Kullanarak `da ikame komutunu verin .
  • Gibi gelişmiş parametre ikame örnekleri${parameter:-word} "kelime" elemanı . "Sözcüğü" tanımı ise :

    Kabuk tarafından birim olarak işlem gören bir karakter dizisi

    - alıntılanan metin ve hatta karışık alıntılar içeren a"b"c'd'e- genişletmelerin gerçek davranışı bundan biraz daha liberal olsa da, örneğin de ${x:-hello world}işe yarıyor.

  • Aritmetik genişleme ile $(( ... )), büyük ölçüde yararsız olmasına rağmen orada (ama yuva komut ikamesi veya değişken açılımları da ve daha sonra bu iç yararlı bir tırnak olabilir). POSIX şunları söylüyor :

    İfade, ifadenin içindeki bir çift tırnak özel olarak ele alınmaması dışında, çift tırnak içine alınmış gibi ele alınacaktır. Kabuk, parametre genişletme, komut değiştirme ve tırnak kaldırma için ifadedeki tüm simgeleri genişletmelidir.

    bu nedenle bu davranış açıkça gereklidir. Bu echo "abc $((4 "*" 5))", globbing yerine aritmetik demektir .

    Eski stil $[ ... ]aritmetik genişletmenin aynı şekilde ele alınmadığına dikkat edin: tırnak işaretleri, genişlemenin alıntılanıp alıntılanmamasına bakılmaksızın, görünürse bir hata olacaktır. Bu form artık belgelenmemiştir ve yine de kullanılması amaçlanmamıştır.

  • İle$"..."" temel öğe olarak kullanılan yerel ayara özgü çeviri . $"tek bir birim olarak ele alınır.

Ayracı genişletme özelliğine sahip, tırnak içermeyen, beklemeyebileceğiniz başka bir yuvalama durumu daha vardır : {a,b{c,d},e}"a bc bd e" ye genişler. ${x:-a{b,c}d}does not Ancak yuva; " a{b,c" ve ardından " " veren bir parametre ikamesi olarak kabul edilir d}. Bu da belgelenmiştir :

Diş telleri kullanıldığında, eşleşen bitiş ayracı, ters eğik çizgi veya tırnak içine alınmış bir dize içinde kaçan ve gömülü bir aritmetik genişletme, komut değiştirme veya parametre genişletme içinde olmayan ilk '}' olur.


Genel bir kural olarak, tüm sınırlandırılmış yapılar bedenlerini çevredeki bağlamdan bağımsız olarak ayrıştırır (ve istisnalar hata olarak değerlendirilir ). Özünde, $(komut değiştirme kodunu görünce ayrıştırıcıdan yeni bir programmış gibi gövdeden neler yapabileceğini sorar ve sonra alt ayrıştırıcı çalıştığında beklenen sonlandırma işaretinin (çıkışsız )veya ))veya }) göründüğünü kontrol eder. tüketebilecekleri şeylerden.

Özyinelemeli bir ayrıştırıcının işleyişini düşünüyorsanız , bu temel duruma basit bir özyineleme. Aslında, dize enterpolasyonunuz olduğunda, diğer yoldan yapmak daha kolaydır. Altta yatan ayrıştırma tekniğinden bağımsız olarak, bu yapıları destekleyen kabuklar aynı sonucu verir.

Bu yapılar üzerinden alıntıyı istediğiniz kadar derine yerleştirebilirsiniz ve beklendiği gibi çalışacaktır. Ortada bir alıntı görerek hiçbir yerde kafanız karışmayacak; bunun yerine, bu, iç bağlamda yeni bir alıntılanan dizenin başlangıcı olacaktır.


Teşekkürler. İçinde "blah/blah\n$(cat "${tmpdir}/${filename}.jpdf")", ikinci çift tırnak neden ilk çift tırnak (yanıtınızda sözdizimi vurgulamasıyla gösterildiği gibi) sonu değil, içerideki bir dizenin başlangıcıdır $(...)? Bunun nedeni, bash ayrıştırıcısının aşağıdan yukarıya yerine yukarıdan aşağıya olmasıdır.
Tim

2
Kullanımında çok fazla varyasyon var "${var-"foo"}"( örneğin Bourne veya Korn kabuğundaki ile echo "${-+"*"}"aynıdır echo *) ve davranış standardın bir sonraki versiyonunda açıkça belirtilmemiş olacaktır . Ayrıca tartışma bakın mail-archive.com/austin-group-l@opengroup.org/msg00167.html
Stéphane Chazelas

3

Belki iki örneğe printf(yerine echo) bakmak yardımcı olacaktır:

$ printf '<%s> ' "(echo " * ")"; echo
<(echo > <test.txt> <ppcg.sh> <file1> <file2> <file3> <)>

Yazdırır (echo (izleyen boşluk dahil ilk sözcük), bazı dosyalar ve kapanış sözcüğü ).
Parantez, alıntılanan dizenin sadece bir parçasıdır (echo .
Yıldız işareti (şimdi çift tırnaklar eşleştirildikçe alıntılanmamıştır), bir glob olarak eşleşen dosyalar listesine genişletilir.
Ve sonra kapanış parantezi.

Ancak, ikinci komutunuz aşağıdaki gibi çalışır:

$ printf '<%s> ' "$(echo " * ")" ; echo
< * >

$Bir komut ikamesi başlar. Bu alıntıyı taze başlatır.
Yıldız işareti alınır " * "ve komut (işte burada alıntılanmış bir dize değil bir komuttur) echoçıktıdır. Son olarak, printfyeniden biçimlendirir *ve olarak yazdırı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.