Bash cố gắng viết hai dấu nhắc shell?


11

Tôi đang xem xét đầu ra của một quy trình bash đang chạy được kết nối với một thiết bị đầu cuối, cho mục đích giáo dục.

Quá trình bash của tôi có PID 2883.

Tôi gõ

[OP@localhost ~]$ strace -e trace=openat,read,write,fork,vfork,clone,execve -p 2883 2> bash.strace

Vào một nhà ga. Sau đó tôi đi vào quy trình bash của mình và có các tương tác sau:

[OP@localhost ~]$ ls

Nhìn vào đầu ra, tôi thấy

strace: Process 2883 attached
read(0, "l", 1)                         = 1
write(2, "l", 1)                        = 1
read(0, "s", 1)                         = 1
write(2, "s", 1)                        = 1
read(0, "\r", 1)                        = 1
write(2, "\n", 1)                       = 1
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fec6b1d8e50) = 3917
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=3917, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
write(1, "\33]0;OP@localhost:~\7", 23) = 23
write(2, "[OP@localhost ~]$ ", 22)  = 22
...

Tôi bối rối ở hai dòng cuối cùng. Có vẻ như bash đang cố gắng viết hai dấu nhắc shell? Những gì đang xảy ra ở đây?

Câu trả lời:


24

Trình <ESC>]0;tự (được hiển thị \33]0;theo strace) là chuỗi thoát để đặt tiêu đề cửa sổ đầu cuối. Nó được kết thúc bằng ký tự BEL ( \7), do đó, đầu tiên writeđặt tiêu đề cửa sổ. Thứ hai in lời nhắc thực tế. Lưu ý rằng ngay cả ngoài chuỗi thoát, chúng không hoàn toàn giống nhau. Dấu nhắc có xung quanh [..]trong khi tiêu đề cửa sổ không.

Chúng ta cũng có thể thấy rằng lần viết đầu tiên chuyển sang thiết bị xuất chuẩn (fd 1, đối số đầu tiên thành write()) và lần thứ hai đến thiết bị lỗi chuẩn. Bash in dấu nhắc đến stderr, vì vậy chữ viết đầu tiên đến từ một nơi khác. Điều đó có lẽ ở đâu đó PROMPT_COMMAND, giống như kịch bản khởi động mặc định của Debian cho Bash. Có một cái gì đó như thế này trong đó:

case "$TERM" in
xterm*|rxvt*)
    PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}: ${PWD}\007"'
    ;;
*)
    ;;
esac

Nó thiết lập rằng PROMPT_COMMANDnếu chạy xtermhoặc rxvt, sẽ hỗ trợ chuỗi thoát đó.


Bạn có biết tại sao bash xuất hiện để đọc mọi thứ theo từng nhân vật, thay vì đọc từng dòng một không? Ngoài ra, tại sao bash viết "l" và "s" vào thiết bị xuất chuẩn? Nếu tôi thực hiện một bước tương tự với cat, có hai điểm khác biệt: nó đọc dòng đầu vào theo dòng và trong khi nó lặp lại đầu vào của nó trở lại thiết bị xuất chuẩn, tôi thấy đầu vào hai lần (một lần khi tôi gõ và một lần khi mèo lặp lại).
extremeaxe5

@ extremeaxe5, về cơ bản là do Bash (hay đúng hơn là thư viện readline) tự xử lý tất cả các xử lý dòng lệnh, thay vì dựa vào xử lý khá hạn chế được thực hiện bởi thiết bị đầu cuối. Nó phải nhận đầu vào ngay lập tức để quyết định phải làm gì khi ví dụ một ký tự TAB hoặc ^A(Ctrl-A) hoặc các ký tự đặc biệt khác nhau được nhấn. Ngoài ra, nó tắt tiếng vang của thiết bị đầu cuối, để nó có thể quyết định đầu ra nào cho mỗi ký tự đầu vào cụ thể (một lần nữa, TAB thường không tạo ra TAB.) catKhông làm điều đó. Nếu bạn, hãy thử chạy dash, không thực hiện bất kỳ xử lý dòng lệnh nào.
ilkkachu

Trên thực tế, lý do Bash gọi read()chỉ đọc một byte mỗi lần là vì nó không thể đọc qua một dòng mới. Dòng mới có thể khiến nó chạy một chương trình bên ngoài, cũng có thể đọc từ cùng một đầu vào. (Và chương trình đó sẽ có thể đọc bất kỳ ký tự nào sau dòng mới.) Nếu không phải quan tâm đến điều đó, nó có thể gọi read()với giới hạn lớn hơn và với thiết bị đầu cuối ở chế độ thô, nó vẫn thường nhận được đầu vào một nhân vật tại một thời điểm (Sẽ tùy thuộc vào việc các nhân vật đầu vào sẽ đến nhanh như thế nào và quá trình được lên lịch như thế nào.)
ilkkachu

Nhận xét thứ hai của bạn dường như chỉ đúng vì Bash tự xử lý dòng lệnh.
extremeaxe5

@ extremeaxe5, vâng, vâng, tôi đã giả sử rằng, vì dù sao đó cũng là trường hợp phổ biến. Nhưng, ngay cả khi hệ vỏ dựa vào chỉnh sửa dòng của thiết bị đầu cuối, thời gian vẫn có thể là một vấn đề. Nếu hai dòng được gửi liên tiếp (nghĩ rằng dán dữ liệu) và hệ thống đã được tải đủ để trình bao không được lên lịch ngay lập tức (hoặc tệ hơn, trình bao bị dừng), thì read()bộ đệm lớn hơn vẫn có thể trả về cả hai dòng cùng một cuộc gọi Tôi không nghĩ có một đảm bảo read()sẽ luôn trả về chỉ một dòng trong chế độ nấu.
ilkkachu
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.