Tại sao dấu chấm than trong dấu ngoặc kép gây ra lỗi Bash?


25

Hãy nhìn vào các lệnh sau:

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

Hai lệnh đầu tiên tạo ra một bong bóng thông báo như mong đợi. Thứ ba cho lỗi hiển thị.

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

Ở đây cũng vậy, các echocông việc cho hai lệnh đầu tiên nhưng không phải trong thứ ba.

Nhiều vấn đề hơn ở đây (mặc dù tôi không có kế hoạch sử dụng cái này): cả hai notify-send "SYNC!TIME"echo "SYNC!TIME"cho bash: !TIME": event not found.

Nhưng cả hai notify-sendecholàm việc với"SYNC! TIME"

Ai đó có thể vui lòng giải thích tại sao bash: !": event not foundlỗi xuất hiện?

Câu trả lời:


31

! là ký tự mở rộng lịch sử mặc định trong Bash, xem phần "MỞ RỘNG LỊCH SỬ" trong phần trang chủ Bash

  • Mở rộng lịch sử không diễn ra nếu !được kèm theo dấu ngoặc đơn, như trong

    notify-send 'SYNC TIME!'
  • Mở rộng lịch sử không diễn ra nếu !được theo sau bởi khoảng trắng, tab, dòng mới, trả lại vận chuyển hoặc =, như trong

    notify-send SYNC TIME!
  • Mở rộng lịch sử làm nơi cất trong

    echo "SYNC TIME!"

    Vì vậy, bạn sẽ gặp lỗi nếu không có lệnh bắt đầu "trong lịch sử của bạn


4
Hành vi sai trái này có thể được khắc phục bằng cách thêm vào .bashrcdòng của bạn set +H. Lưu ý rằng !đã không đặc biệt trong kịch bản; coi nó là đặc biệt sẽ phá vỡ nhiều kịch bản tuân thủ tiêu chuẩn. Nó chỉ được coi là "đặc biệt" trong các vỏ tương tác và chỉ theo mặc định cho đến khi bạn sửa nó. :-)
R ..

15

Bởi vì trong bash, !là một từ dành riêng (OK, ký tự), nó có ý nghĩa đặc biệt trong các ngữ cảnh khác nhau. Trong trường hợp cụ thể này, bạn đang rơi vào tầm quan trọng của nó trong tìm kiếm lịch sử. Từ 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.

Về cơ bản, điều này có nghĩa là bash sẽ lấy các ký tự sau !và tìm kiếm lịch sử của bạn cho lệnh đầu tiên mà nó tìm thấy bắt đầu bằng các ký tự đó. Nó dễ chứng minh hơn là giải thích:

$ echo foo
foo
$ !e
echo foo
foo

Việc !mở rộng lịch sử được kích hoạt, khớp với lệnh đầu tiên bắt đầu bằng lệnh echạy trước echo foođó được chạy lại. Vì vậy, khi bạn viết "SYNC TIME!", bash đã thấy !", tìm kiếm lịch sử cho một lệnh bắt đầu bằng ", thất bại và phàn nàn về nó. Bạn có thể nhận được cùng một lỗi bằng cách chạy, ví dụ !nocommandstartswiththis.

Để in dấu chấm than, bạn cần thoát nó theo một trong hai cách sau:

echo 'Hello world!'
echo Hello world\!

6
echo Hello world!không hoạt động --- mở rộng lịch sử không được kích hoạt bởi khoảng trống ;-) (ah, các trường hợp góc đẹp ...)
Rmano

1
Mặc dù vậy, tốt hơn là dựa vào việc thoát khỏi dấu chấm than hơn là khoảng trống.
Ehtesh Choudhury

1
@Rmano Tôi thích nói một cách rõ ràng hơn là hướng dẫn tôi một hành vi có thể thay đổi
Braiam

!là một từ dành riêng trong bash, vì POSIX cũng tuyên bố . Nhưng tôi khá chắc chắn rằng tình trạng của nó là hoàn toàn không liên quan đến vai trò của nó trong việc mở rộng lịch sử và không liên quan đến việc đối xử với nó trong dấu ngoặc kép. !là một từ dành riêng vì nó phủ nhận trạng thái thoát của lệnh / đường ống, vì vậy nó không thể được sử dụng làm lệnh. Không có từ dành riêng khác là đặc biệt trong một "bối cảnh -quoted, trong khi $không một từ dành riêng nhưng được đối xử đặc biệt.
Eliah Kagan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.