Mengapa tanda seru dalam tanda kutip ganda menyebabkan kesalahan Bash?

25

Silakan lihat perintah-perintah ini:

$ notify-send SYNC TIME!
$ notify-send 'SYNC TIME!'
$ notify-send "SYNC TIME!"
bash: !": event not found
$

Dua perintah pertama menghasilkan gelembung pemberitahuan seperti yang diharapkan. Yang ketiga menunjukkan kesalahan.

dan

$ echo SYNC TIME!
SYNC TIME!
$ echo 'SYNC TIME!'
SYNC TIME!
$ echo "SYNC TIME!"
bash: !": event not found
$

Di sini juga, echoberfungsi untuk dua perintah pertama tetapi tidak pada yang ketiga.

Lebih banyak masalah di sini (meskipun saya tidak berencana menggunakan ini): keduanya notify-send "SYNC!TIME"dan echo "SYNC!TIME"memberi bash: !TIME": event not found.

Tapi keduanya notify-senddan echobekerja dengan"SYNC! TIME"

Dapatkah seseorang tolong jelaskan mengapa bash: !": event not foundkesalahan itu muncul?

Keadilan untuk Monica
sumber

Jawaban:

31

!adalah karakter perluasan riwayat default di Bash, lihat bagian "SEJARAH EKSPANSI" di halaman manual Bash

  • Ekspansi sejarah tidak terjadi jika !diapit oleh tanda kutip tunggal, seperti pada

    notify-send 'SYNC TIME!'
  • Perluasan riwayat tidak terjadi jika !diikuti oleh spasi, tab, baris baru, carriage return, atau =, seperti pada

    notify-send SYNC TIME!
  • Ekspansi sejarah memang terjadi di

    echo "SYNC TIME!"

    Jadi, Anda akan mendapatkan kesalahan jika tidak ada perintah yang dimulai dengan "dalam riwayat Anda

Florian Diesch
sumber
4
Kesalahan perilaku ini dapat diperbaiki dengan menambahkan .bashrcgaris Anda set +H. Catatan yang !sudah non-khusus dalam skrip; memperlakukannya sebagai istimewa akan mematahkan banyak skrip yang memenuhi standar. Ini hanya diperlakukan sebagai "spesial" di shell interaktif, dan hanya secara default sampai Anda memperbaikinya. :-)
R ..
15

Karena dalam bash, !adalah kata khusus (OK, karakter), ia memiliki arti khusus dalam konteks yang berbeda. Dalam kasus khusus ini, Anda tidak setuju dengan pentingnya pencarian riwayat. Dari man bash:

   History expansions introduce words from the history list into the input
   stream, making it easy to repeat commands, insert the  arguments  to  a
   previous command into the current input line, or fix errors in previous
   commands quickly.

  [...]

   History expansions are introduced by
   the appearance of the  history  expansion  character,  which  is  !  by
   default.   Only  backslash  (\) and single quotes can quote the history
   expansion character.

Pada dasarnya, ini berarti bahwa bash akan mengambil karakter setelah !dan mencari sejarah Anda untuk perintah pertama yang ditemukan yang dimulai dengan karakter tersebut. Lebih mudah ditunjukkan daripada dijelaskan:

$ echo foo
foo
$ !e
echo foo
foo

The !ekspansi sejarah diaktifkan, yang cocok perintah pertama dimulai dengan eyang sebelumnya dijalankan echo fooyang kemudian jalankan lagi. Jadi, ketika Anda menulis "SYNC TIME!", bash melihat !", mencari sejarah untuk perintah yang dimulai dengan ", gagal dan mengeluh tentang hal itu. Anda bisa mendapatkan kesalahan yang sama dengan menjalankan, misalnya !nocommandstartswiththis.

Untuk mencetak tanda seru, Anda harus menghindarinya dengan salah satu dari dua cara berikut:

echo 'Hello world!'
echo Hello world\!
terdon
sumber
6
echo Hello world!tidak bekerja --- ekspansi sejarah tidak dipicu oleh kekosongan ;-) (ah, kasus sudut yang bagus ...)
Rmano
1
Namun, lebih baik mengandalkan keluar dari tanda seru daripada yang kosong.
Ehtesh Choudhury
1
@Rmano Saya lebih suka mengatakannya secara eksplisit daripada membimbing saya tentang perilaku yang mungkin untuk diubah
Braiam
!adalah kata cipta di bash, seperti POSIX juga menyatakan . Tapi saya cukup yakin statusnya sebagai orang sama sekali tidak terkait dengan perannya dalam ekspansi sejarah dan tidak relevan dengan perlakuannya dalam tanda kutip ganda. !adalah kata yang dilindungi undang-undang karena meniadakan status keluar perintah / jalur pipa, sehingga tidak dapat digunakan sebagai perintah. Tidak ada kata reserved lain adalah khusus dalam "konteks -quoted, sementara $ini tidak kata cipta tapi yang diperlakukan khusus.
Eliah Kagan