Ünlem işareti neden bazen!


14

Bunun !komut satırı geçmişi bağlamında komut satırı üzerinde özel bir önemi olduğunu anlıyorum , ancak bunun dışında, bir çalışan komut dosyasında ünlem işareti bazen bir ayrıştırma hatasına neden olabilir.
Bence bunun bir ilgisi var event, ama bir olayın ne olduğu ya da ne yaptığı hakkında hiçbir fikrim yok. Yine de, aynı komut farklı durumlarda farklı davranabilir.
Aşağıdaki son örnek bir hataya neden olur; ama neden, aynı kod komut değiştirme dışında çalıştığında? .. GNU bash kullanarak 4.1.5

# This works, with or without a space between ! and p
  { echo -e "foo\nbar" | sed -nre '/foo/! p'
    echo -e "foo\nbar" | sed -nre '/foo/!p'; }
# bar
# bar

# This works, works when there is a space between ! and p
  var="$(echo -e "foo\nbar" | sed -nre '/foo/! p')"; echo "$var"
# bar

# This causes an ERROR, with NO space between ! and p
  var="$(echo -e "foo\nbar" | sed -nre '/foo/!p')"; echo "$var"
# bash: !p': event not found


@Warren .. Teşekkürler. QA'yı görmüştüm, ama gerçekten sadece ters eğik
çizgiden nasıl kaçacağından bahsediyor

@fred: "görünüşe göre kaçtı"? Hiç kaçış görmüyorum ve çift tırnak kullanıyorsunuz. (Düzeltilmiş) yanıtıma bakın. Sizce hangi kısım kaçtı?
Caleb

@Caleb. Evet, yanlış terimi kullandım .. protecteddaha uygun olurdu. ('tek tırnak' tarafından korunmaktadır)
Peter.O

Sadece basit ödevlerle ilgileniyorsanız, var=$(…)(çift tırnak yok) kullanabilirsiniz ve beklediğiniz gibi çalışacaktır (sanırım). Bu hala “güvenlidir” çünkü basit bir ödevin değer kısmı sözcük bölme veya globbing'e tabi değildir (ancak bu, tüm kabuklar altında yerleşikler (örn export. local, Vb.) Aracılığıyla yapılan ödevler için doğru olmayabilir ). Ne yazık ki, bu basit ödevlerin ötesine geçmez, çünkü çift tırnaklar, diğer bağlamlarda diğer genişleme türlerini alırken, kelime bölünmesine ve globbing'e karşı korunmanın bir yoludur.
Chris Johnsen

Yanıtlar:


12

!Karakter bash geçmişi ikame çağırır. Ardından bir dize (başarısız örneğinizde olduğu gibi) söz konusu dize ile başlayan son geçmiş etkinliğine genişletmeye çalışır. Tıpkı $varo dizenin değerine genişletildiği gibi !echo, geçmişinizdeki son yankı komutuna da genişler.

Uzay, bu açılımlarda bir kırılma karakteri. İlk olarak bunun değişkenlerle nasıl çalışacağına dikkat edin:

# var="like"
# echo "$var"
like
# echo "$"
$
# echo "Do you $var frogs?"
Do you like frogs?       <- as expected, variable name broken at space
# echo "Do you $varfrogs?"
Do you?                  <- $varfrogs not defined, replaced with blank
# echo "Do you $ var frogs?"
Do you $ var frogs?      <- $ not a valid variable name, ignored

Tarih genişlemesi için de aynı şey olacaktır. Bang karakteri ( !), bir tarih değiştirme dizisini başlatır, ancak yalnızca bir dize gelirse. Bir boşlukla onu, bir değiştirme dizisinin parçası yerine gerçek bir patlama haline getirin.

Tek tırnak kullanarak hem değişken hem de geçmiş genişlemesi için bu tür bir değiştirmeyi önleyebilirsiniz. İlk örnekleriniz tek tırnak kullandılar ve bu yüzden iyi çalıştı. Son örnekleriniz çift tırnak içinde ve böylece bash onları başka bir şey yapmadan genişleme dizileri için taradı. Birincisinin açmadığı tek neden, boşluğun yukarıda gösterildiği gibi bir kırılma karakteri olmasıdır.


Teşekkürler Caleb .. Ön konseptlerimden bir diğeri reddedildi ... Ben bas ayrıştırma en içteki parantez veya küme ayrağından yapıldığını düşündüm ve sonra dışarı doğru çalıştı ... Görünüşe göre bash farklı ayrışıyor.
Peter.O

1
Tırnak işareti iç içe dizelerde değişmeden bash'de yeterince kafa karıştırıcıdır. Olduğu gibi, değişimler çok erken bir süreçte gerçekleşir. Bu örneği düşünün:var=word; echo "test '$var'"; echo 'test "$var"'
Caleb

.. Evet undestood. Tırnakları tırnak içine yerleştirmenin farkındaydım ... Yanlış anladığım, komut değiştirme parantezleri içindeki kodun, bu parantezleri çevreleyen şeye ayrı ayrı ayrıştırılacağını düşündüm; ama görünüşe göre değil .. teşekkürler.
Peter.O

6

Zaten şöyle Caleb söylediği , !çağırmak bash geçmişi ikamesi için kullanılır.

Benim gibi böyle bir özelliğe ihtiyacınız olmadığını düşünüyorsanız, aşağıdaki satırı ekleyerek devre dışı bırakabilirsiniz ~/.bashrc:

set +H

Buna ihtiyacım yok, çünkü tarih yukarı ok ve Ctrl- rartımlı ters arama ile kurtarılabilir . Kısayolların ayrıntılı listesi için bash kılavuz sayfasına, Geçmişi Yönetme Komutları bölümüne bakın .


2
Onsuz nasıl yaşıyorsun !!?
Caleb

Teşekkürler., Ben bu bir sorun olabilir, taşınabilirlik açısından akıllıca olabilir, ama set +Hsenaryoda kullanmak da iyi çalışıyor :) +1
Peter.O

2
@fred: garip, genellikle tarih genişlemesi sadece interaktif mermiler için "açık".
enzotib

@enzo .. Tekrar teşekkürler .. Bunu komut satırından test etmiştim .. Ah! Öğrenmek çok eğlenceli olmasaydı, sıkıcı olurdu ... kahveden bahsettim mi? çok yardımcı olur :)
Peter.O

Ya, bu bir yakaladım. Yapıştırılan kodu sadece bu nedenle komut satırında başarısız olan komut dosyalarından kopyaladım. Tarih genişletmesi senaryoda bir endişe değildi, ancak etkileşimli bir kabukta.
Caleb

2

ilk örneğiniz:

{ echo -e "foo\nbar" | sed -nre '/foo/! p'
    echo -e "foo\nbar" | sed -nre '/foo/!p'; }

düşürülebilir

echo '! p' 
echo '!p'

Tek tırnak içinde, tüm karakterler değişmez değerlerini korur. Böylece !özel anlamını yitirmiş ve tarih genişlemesi önceden şekillenmemiştir.

ikinci ve üçüncü örnekleriniz:

var="$(echo -e "foo\nbar" | sed -nre '/foo/! p')"; echo "$var"

var="$(echo -e "foo\nbar" | sed -nre '/foo/!p')"; echo "$var"

düşürülebilir

echo "'! p'"

echo "'!p'"

'! p've '!p'esasen çift tırnaklı dizelerin parçalarıdır.

Çift tırnak içinde tüm karakterler kendi değişmez değerleri korumak hariç $ , `, \ve !.

Yani tek tırnak ima '! p've '!p'onların özel anlamları kaybetmiş (yani: kaçamayan !) ama !yine de böylece geçmiş genişleme yapılır özel anlamı korur.

Ancak, !bir boşluk karakteri izlediğinde, geçmiş genişletmesi gerçekleştirilmez.

Alıntı man bash:

ALINTI YAPMAK

[...]

Karakterleri tek tırnak içine almak tırnak içindeki her karakterin gerçek değerini korur. [...]

Karakterleri çift tırnak içine almak $, `, \ ve tarih genişletmesi etkinleştirildiğinde! Hariç, tırnak içindeki tüm karakterlerin gerçek değerini korur. [...] Etkinleştirilirse, tarih genişlemesi bir! çift ​​tırnak içinde görünen bir ters eğik çizgi kullanılarak kaçar. Önündeki ters eğik çizgi! kaldırılmaz.

TARİH GENİŞLEME

[...]

Tarih genişlemeleri, tarih genişleme karakterinin ortaya çıkmasıyla ortaya çıkar! varsayılan olarak. Yalnızca ters eğik çizgi (\) ve tek tırnaklar, geçmiş genişletme karakterini alıntılayabilir.

Birkaç karakter, geçmiş genişleme karakterinden hemen sonra bulunursa, tarih genişlemesini engelliyor olsa bile, boşluk, sekme, yeni satır, satır başı ve =. Extglob kabuk seçeneği etkinleştirilirse (ayrıca genişletmeyi de engeller.

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.