Bash'ta joker karakter / yıldız işaretinden nasıl kaçarım?


136

Örneğin:

me$ FOO="BAR * BAR"
me$ echo $FOO
BAR file1 file2 file3 file4 BAR

ve \kaçış karakterini kullanarak :

me$ FOO="BAR \* BAR"
me$ echo $FOO
BAR \* BAR

Açıkçası aptalca bir şey yapıyorum.

Çıktıyı nasıl alabilirim BAR * BAR?

Yanıtlar:


139

Ayar $FOOyeterli olmadığında alıntı yapılıyor. Değişken referansını da belirtmeniz gerekir:

me$ FOO="BAR * BAR"
me$ echo "$FOO"
BAR * BAR

8
bu gizemli, neden bu? ne oluyor?
tofutim

Çünkü değişkenler genişliyor
Daniel

103

KISA CEVAP

Diğerlerinin söylediği gibi - garip davranışı önlemek için her zaman değişkenleri alıntılamanız gerekir. Kullanım Yani "$ foo" echo yerine sadece $ foo echo .

UZUN CEVAP

Bence bu örnek daha fazla açıklamayı gerektiriyor, çünkü yüzünde göründüğünden daha fazlası var.

Karışıklıklarınızın nereye geldiğini görebiliyorum çünkü ilk örneğinizi çalıştırdıktan sonra muhtemelen kendinize kabuğun açıkça yaptığını düşündünüz:

  1. Parametre genişletme
  2. Dosya adı genişletmesi

İlk örneğinizden:

me$ FOO="BAR * BAR"
me$ echo $FOO

Parametre genişletmeden sonra:

me$ echo BAR * BAR

Ve dosya adı genişletmesinden sonra:

me$ echo BAR file1 file2 file3 file4 BAR

Ve sadece echo BAR * BARkomut satırına yazarsanız, bunların eşdeğer olduğunu görürsünüz.

Muhtemelen kendinize "* 'dan kaçarsam, dosya adının genişletilmesini önleyebilirim" diye düşündünüz.

İkinci örneğinizden:

me$ FOO="BAR \* BAR"
me$ echo $FOO

Parametre genişletmeden sonra:

me$ echo BAR \* BAR

Ve dosya adı genişletmesinden sonra:

me$ echo BAR \* BAR

Doğrudan komut satırına "echo BAR \ * BAR" yazmayı denerseniz, gerçekten de "BAR * BAR" yazılır çünkü dosya adı genişletmesi kaçış tarafından önlenir.

Peki neden $ foo kullanmak işe yaramadı?

Çünkü üçüncü bir genişleme var - Alıntı Kaldırma. Bash manuel teklif kaldırma:

Önceki genişletmelerden sonra, yukarıdaki genişletmelerden birinden kaynaklanmayan '\', '' 've' "'karakterlerinin alıntılanmamış tüm oluşumları kaldırılır.

Yani komutu doğrudan komut satırına yazdığınızda kaçış karakteri önceki bir genişletmenin sonucu değildir, bu yüzden BASH echo komutuna göndermeden önce onu kaldırır, ancak 2. örnekte "\ *" önceki bir Parametre genişletmesinin sonucu, bu yüzden kaldırılmaz. Sonuç olarak, echo "\ *" alır ve yazdırdığı şey budur.

İlk örnek - "*" arasındaki farkın Alıntı Kaldırma ile kaldırılacak karakterlere dahil olmadığını unutmayın.

Umarım bu mantıklı gelir. Sonunda sonuç aynı - sadece tırnak kullanın. Sadece Parametre ve Dosya adı genişletmesi oyundaysa mantıklı bir şekilde çalışması gereken neden kaçmayı açıklayacağımı düşündüm.

BASH genişletmelerinin tam açıklaması için bakınız:

http://www.gnu.org/software/bash/manual/bashref.html#Shell-Expansions


1
Mükemmel cevap! Şimdi böyle aptalca bir soru sorduğumu hissetmiyorum. :-)
andyuk

1
Özel karakterlerden kaçmak için bazı araçlar var mı?
Jigar Joshi

"tuhaf davranışı önlemek için değişkenleri her zaman alıntılamalısınız" - bunları dize olarak kullanmak istediğinizde
Angelo


Yukarıdaki @finnw tarafından verilen cevap doğrudan soruyu cevaplasa da, bu sorunun nedenini açıklamakta çok daha iyidir , bu da kendi senaryolarımızda kullanmanın nasıl çalışacağını tahmin etmede çok daha fazla yardımcı olur. Bu daha fazla görmemiz gereken bir cevap.
Nicolas Coombs

54

Bu eski konuya biraz ekleyeceğim.

Genellikle

$ echo "$FOO"

Ancak, bu sözdiziminde bile sorun yaşadım. Aşağıdaki komut dosyasını düşünün.

#!/bin/bash
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1"

*İhtiyaçlar için aynen geçirilecek curlancak aynı sorunlar ortaya çıkacaktır. Yukarıdaki örnek çalışmaz (geçerli dizindeki dosya adlarına genişler) ve ikisi de çalışmaz \*. Ayrıca $curl_optstek bir (geçersiz) seçenek olarak tanınacağı için teklif veremezsiniz curl.

curl: option -s --noproxy * -O: is unknown
curl: try 'curl --help' or 'curl --manual' for more information

Bu nedenle , global kalıba uygulanırsa dosya adının genişletilmesini tamamen önlemek için bashdeğişkenin $GLOBIGNOREkullanılmasını veya set -fyerleşik bayrağı kullanmasını öneririm .

#!/bin/bash
GLOBIGNORE="*"
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1"  ## no filename expansion

Orijinal örneğinize başvurma:

me$ FOO="BAR * BAR"

me$ echo $FOO
BAR file1 file2 file3 file4 BAR

me$ set -f
me$ echo $FOO
BAR * BAR

me$ set +f
me$ GLOBIGNORE=*
me$ echo $FOO
BAR * BAR

4
Harika bir açıklama, teşekkürler! Benim kullanımım SELECT * FROM etc., çalışan tek yol budur.
knutole

1
Set -f çözümünü sunduğunuz için teşekkür ederiz!
hachre

5
FOO='BAR * BAR'
echo "$FOO"

Bu işe yarar, ancak ilk satırdaki tek tırnak işaretlerini çift tırnak olarak değiştirmek gerekli değildir.
finnw

1
Hayır, değil, ancak özel kabuk karakterleri ekleyeceğinizde ve herhangi bir ikame istemediğinizde tek tırnak kullanma alışkanlığı tercih edilir.
tzot


3

Komut satırından printfziyade kullanma alışkanlığına girmeye değer olabilir echo.

Bu örnekte fazla fayda sağlamaz, ancak daha karmaşık çıktılar için daha yararlı olabilir.

FOO="BAR * BAR"
printf %s "$FOO"

Neden cehennemde? printf ayrı bir işlemdir (en azından bash içinde yerleşik değildir) ve gösterdiğiniz printf kullanımının ekoya göre hiçbir faydası yoktur.
ddaa

2
printf olan bir bash dahili ve ben çok fayda vermez Bu örnekte benim cevap" dedin ama daha karmaşık çıkışı ile daha yararlı olabilir.
Dave Webb
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.