เหตุใดเครื่องหมายอัศเจรีย์ภายในเครื่องหมายคำพูดคู่จึงทำให้เกิดข้อผิดพลาด Bash


25

โปรดดูคำสั่งเหล่านี้:

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

สองคำสั่งแรกสร้างบับเบิลการแจ้งเตือนตามที่คาดไว้ ข้อที่สามแสดงข้อผิดพลาด

และ

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

ที่นี่เช่นกันการechoทำงานสำหรับสองคำสั่งแรก แต่ไม่ใช่ในคำสั่งที่สาม

ปัญหาเพิ่มเติมที่นี่ (แม้ว่าฉันไม่ได้วางแผนในการใช้นี้): ทั้งสองnotify-send "SYNC!TIME"และให้echo "SYNC!TIME"bash: !TIME": event not found

แต่ทั้งคู่notify-sendและechoทำงานด้วย"SYNC! TIME"

ใครช่วยกรุณาอธิบายว่าทำไมbash: !": event not foundข้อผิดพลาดปรากฏขึ้น?

คำตอบ:


31

!เป็นอักขระส่วนขยายประวัติเริ่มต้นใน Bash ดูส่วน "การขยายประวัติ" ในBash manpage

  • การขยายประวัติจะไม่เกิดขึ้นหาก!มีการปิดล้อมด้วยเครื่องหมายคำพูดเดี่ยวเช่นเดียวกับใน

    notify-send 'SYNC TIME!'
  • การขยายประวัติจะไม่เกิดขึ้นหาก!มีการเว้นวรรคตามด้วยแท็บขึ้นบรรทัดใหม่การขึ้นบรรทัดใหม่หรือ=ตามที่

    notify-send SYNC TIME!
  • การขยายตัวของประวัติศาสตร์ไม่ใช้สถานที่ใน

    echo "SYNC TIME!"

    ดังนั้นคุณจะได้รับข้อผิดพลาดหากไม่มีคำสั่งเริ่มต้นด้วย"ในประวัติของคุณ


4
พฤติกรรมที่ไม่เหมาะสมนี้สามารถแก้ไขได้โดยเพิ่มลง.bashrcในบรรทัดของset +Hคุณ โปรดทราบว่า!ไม่ใช่สคริปต์พิเศษในการเขียนสคริปต์ ถือว่าเป็นพิเศษจะทำลายสคริปต์ตามมาตรฐานจำนวนมาก มันจะถือว่าเป็น "พิเศษ" ในเชลล์แบบโต้ตอบและโดยค่าเริ่มต้นเท่านั้นจนกว่าคุณจะแก้ไข :-)
อา

15

เนื่องจากใน bash !เป็นคำที่สงวนไว้ (OK, character) จึงมีความหมายพิเศษในบริบทที่แตกต่างกัน ในกรณีนี้คุณกำลังตกหลุมสำคัญในการค้นหาประวัติ จาก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.

โดยทั่วไปสิ่งนี้หมายความว่าทุบตีจะนำตัวละครหลังจาก !และค้นหาประวัติของคุณสำหรับคำสั่งแรกที่พบว่าเริ่มต้นด้วยตัวอักษรเหล่านั้น มันง่ายกว่าที่จะสาธิตมากกว่าอธิบาย:

$ echo foo
foo
$ !e
echo foo
foo

การ!ขยายประวัติที่เปิดใช้งานซึ่งตรงกับคำสั่งแรกที่เริ่มต้นด้วยeการทำงานก่อนหน้านี้echo fooซึ่งถูกเรียกใช้อีกครั้ง ดังนั้นเมื่อคุณเขียน"SYNC TIME!"ทุบตีดู!"ประวัติค้นหาคำสั่งที่เริ่มต้นด้วย"ล้มเหลวและบ่นเกี่ยวกับมัน !nocommandstartswiththisคุณจะได้รับข้อผิดพลาดเดียวกันโดยใช้ตัวอย่างเช่น

หากต้องการพิมพ์เครื่องหมายอัศเจรีย์คุณต้องหลีกเลี่ยงด้วยวิธีใดวิธีหนึ่งต่อไปนี้:

echo 'Hello world!'
echo Hello world\!

6
echo Hello world!ทำงาน --- การขยายประวัติไม่ได้ถูกกระตุ้นด้วยช่องว่าง ;-) (อา, กรณีมุมที่ดี ... )
Rmano

1
แม้ว่าจะเป็นการดีกว่าที่จะพึ่งพาเครื่องหมายอัศเจรีย์มากกว่าช่องว่าง
Ehtesh Choudhury

1
@Rmano ฉันชอบที่จะพูดอย่างนั้นอย่างชัดเจนมากกว่าคู่มือฉันของพฤติกรรมว่ามันเป็นไปได้ที่จะเปลี่ยนแปลง
Braiam

!เป็นคำที่สงวนไว้ในbashขณะที่POSIX ก็ประกาศเช่นกัน แต่ฉันค่อนข้างแน่ใจว่าสถานะของการเป็นหนึ่งไม่เกี่ยวข้องกับบทบาทในการขยายประวัติศาสตร์และไม่เกี่ยวข้องกับการรักษาด้วยคำพูดสองครั้ง !เป็นคำที่สงวนไว้เนื่องจากไม่ได้ใช้สถานะการออกของคำสั่ง / ไปป์ไลน์จึงไม่สามารถใช้เป็นคำสั่งได้ ไม่มีคำสงวนอื่น ๆ เป็นพิเศษใน"บริบท -quoted ในขณะที่$เป็นไม่ได้คำสงวน แต่จะได้รับการปฏิบัติเป็นพิเศษ
Eliah Kagan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.