Bir patlama nasıl yankılanır!


59

echoİçeriği bir dosyaya yerleştirerek, bir editörle açmak yerine bir script oluşturmaya çalıştım.

echo -e "#!/bin/bash \n /usr/bin/command args"  > .scripts/command

çıktı :

bash:! / bin / bash: olay bulunamadı

Bu garip davranışı patlamaya izole ettim .

$ echo !
!  

$ echo "!"
bash: !: event not found

$ echo \#!
#!

$ echo \#!/bin/bash
bash: !/bin/bash: event not found
  • Neden patlama buna neden oluyor?
  • Bash'ın ifade ettiği bu “olaylar” nelerdir?
  • Bu sorunu nasıl alıp ekrana veya dosyama "#! / Bin / bash" yazdırabilirim?

Yanıtlar:


59

Tek tırnak kullanmayı deneyin.

echo -e '#!/bin/bash \n /usr/bin/command args'  > .scripts/command

echo '#!'

echo '#!/bin/bash'

Sorun oluşuyor çünkü bash! / Bin / bash için geçmişini arıyor. Tek tırnak kullanmak bu davranıştan kaçar.


3
Ayrıca dize enterpolasyonunu devre dışı bırakır :(
Hubro

Mesele bu! İkili tırnak işaretleri kullanarak aynı yankı komutuna başka argümanlar ekleyebilir ve bu kısımlar üzerinde enterpolasyon alabilirsiniz.
Richm

3
C stilini bash ile birlikte de kullanabilirsiniz $''. Örneğin, echo $'1!\n2!\n3!\n'her numarayı, ardından bir patlama ve bir yeni satır izler.
spelufo

1
Tek tırnak içine bang koymak çok satırlı bir dizeye girerken bana yardımcı olmadı:! bash — tek tırnak (gerçek anlamda) patlamadan
kaçmaz

1
@kbolino Haklısın. Bu nedenle tam örnek dizge şöyle olacaktır: echo '0123'"$var"'456'İki tekli tırnak çiftini ve bir çift çift tırnak çiftine dikkat edin.
phyatt

16

As Richm söyledi , bash bir yapmaya çalışıyor geçmişi maçı . Bundan kaçınmanın bir başka yolu da patlama ile kaçmaktır \:

$ echo \#\!/bin/bash
#!/bin/bash

Çift tırnak içinde \olduğunu unutmayın, ancak kaldırılmaz:

$ echo "\!"
\!

8
Kısacası, "\!"aslında , POSIX uyumluluğu için sözde \!olmayan genişler !.
Mikel

@Mikel Sigh. Zsh ve bash arasındaki pek çok fark
Michael Mrozek

Bu çok satırlı bir dizeye girerken bile çalışır. 1. patlamaya girerken bir dizgenin dışında olmayı ve 2. bu yöntemi (ters eğik çizgi) kullanmanın çoğu senaryoda en az sürprizle çalıştığını hatırlayın.
binki

1
Bir betiği çalıştırırken bash’ın geçmiş arama yapmadığını not etmek yararlı olacaktır, bu yüzden orada özel bir şey yapmanıza gerek yoktur.
binki

!Sihirli davranış olmadan çift tırnak içinde kaçmak için herhangi bir sözdizimi yok mu? Bu fındık ...
Lassi

13

!bir tarih değiştirme işlemini başlatır (bir "olay" komut geçmişindeki bir satırdır); örneğin !ls, içeren son komut satırına genişler lsve içeren son komut satırına !?foogenişler foo. Ayrıca, belirli kelimeleri (örneğin !!:1önceki komutun ilk kelimesini ifade eder) ve daha fazlasını da çıkarabilirsiniz ; Ayrıntılar için kılavuza bakınız.

Bu özellik, komut satırının ilkel olduğu günlerde önceki komutları hızlıca geri çağırmak için icat edildi. Modern kabukları (en azından bash ve zsh) ve kopyala ve yapıştır ile, tarihin genişlemesi eskisi kadar yararlı değildir - yine de yararlıdır, ancak onsuz da alabilirsiniz.

histcharsDeğişkeni ayarlayarak hangi karakterin tarih değiştirmeyi tetikleyeceğini değiştirebilirsiniz ; nadiren tarih ikame kullanıyorsanız, örneğin ayarlayabilirsiniz histchars='¡^'böylece ¡yerine geçmiş genişlemesini tetikleyen !. Özelliği tamamen birlikte bile kapatabilirsiniz set +o histexpand.


3

Belirli bir komut satırında geçmiş genişletmeyi devre dışı bırakabilmek spaceiçin, 3. karakter olarak kullanabilirsiniz $histchars:

histchars='!^ '

Ardından, komutunuza bir satır boşluğu girerseniz, tarih genişletme işlemi gerçekleştirilmez.

bash-4.3$ echo "#!/bin/bash"
bash: !/bin/bash: event not found
bash-4.3$  echo "#!/bin/bash"
#!/bin/bash

Zaman önündeki boşlukların da kullanılabilir olduğu unutulmamalıdır $HISTCONTROLiçeren ignorespaceanlatmak için bir yol olarak bashtarihte bir komut satırı kaydetmek için değil.

Her iki özelliğin de dahili olarak olmasını istiyorsanız, 3. karakteri olarak başka bir karakter seçmeniz gerekir $histchars. Komutunun yorumlanma şeklini etkilemeyecek birini istiyorsun. Birkaç seçenek:

  • ters eğik çizgi ( \) kullanma : \echo fooçalışır ancak takma adı veya anahtar sözcükleri devre dışı bırakmanın yan etkisi vardır.
  • SEKME: İlk sıraya yerleştirmek için Ctrl+VTabolsa da basmanız gerekir .
  • Eğer iki anahtar yazarak sakıncası yoksa, normalde ilk konumda görünür (olmayan herhangi bir karakter seçebilirsiniz %, @, ?, kendiniz oluşturun) ve bunun için boş bir takma ad olun:

    histchars='!^%'
    alias %=
    

    Ardından bu karakteri, boşluğu ve komutunuzu girin:

    bash-4.3$ % echo !!
    !!
    

(Eğer tarih ikamesi olsa devre dışı bırakılmış bir komutu kaydetmek için değil mümkün olmayacaktır. Ayrıca varsayılan 3 karakter unutmayın $histcharsolduğunu #bunu değiştirirseniz. böylece tarih genişleme yorumlarda yapılmaz ve en yorumları girin istemi, orada! dizilerinin orada genişletilebileceği gerçeğinin farkında olmalısınız).


2

Önerilen çözümler, örneğin aşağıdaki örnekte çalışmaz:

$ bash -c "echo 'hello World!'"
-bash: !'": event not found
$

Bu durumda patlama, octal ASCII kodu kullanılarak basılabilir:

$ bash -c "echo -e 'hello World\0041'"
hello World!
$

1
İlk komutunuzda, !diğer cevapların tarih genişleme anlamını koruduğunu gösterdiği gibi, çift tırnak arasındadır. Sen kullanabilirsiniz bash -c 'echo '\''hello World!'\'veya bash -c "echo 'hello World"\!"'".
Gilles 'SO- kötülük' dur '26

-İ parametresi olmadan ortam değiştirilebilir (~ / .profile dosyanızın çalışmadığı gibi). Ancak, -i yapmak, aslında çalıştırmak istediğiniz komutun çıktısında .profile dosyasındaki herhangi bir çıkışın yakalanacağı anlamına gelir.
Brent,

-1

Bash 4.3'ten bu yana, artık geçmiş genişletme karakterini alıntılamak için çift tırnak kullanabilirsiniz:

$ bash --version
GNU bash, version 4.3...
[...]
$ echo "Hello World!"
Hello World!

4
Hayır, sadece !bunu takip "eden artık özel değil. echo "foo!"Tamam, echo "foo!bar"değil
Stéphane Chazelas
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.