Bash mencoba menulis dua prompt shell?

11

Saya melihat output strace dari proses bash berjalan yang terhubung ke terminal, untuk tujuan pendidikan.

Proses bash saya memiliki PID 2883.

saya mengetik

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

Ke dalam terminal. Saya kemudian pergi ke proses bash saya, dan memiliki interaksi berikut:

[OP@localhost ~]$ ls

Melihat hasilnya, saya mengerti

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
...

Saya bingung pada dua baris terakhir. Tampaknya bash sedang mencoba untuk menulis dua prompt shell? Apa yang terjadi di sini?

extremeaxe5
sumber

Jawaban:

24

The <ESC>]0;urutan (ditampilkan sebagai \33]0;oleh strace) adalah urutan escape untuk mengatur judul jendela terminal. Itu diakhiri dengan karakter BEL ( \7), jadi yang pertama writemengatur judul jendela. Yang kedua mencetak prompt yang sebenarnya. Perhatikan bahwa meskipun terlepas dari urutan pelarian, mereka tidak persis sama. Prompt memiliki sekitarnya [..]sementara judul jendela tidak.

Kita juga dapat melihat bahwa penulisan pertama adalah stdout (fd 1, argumen pertama write()), dan yang kedua ke stderr. Bash mencetak prompt ke stderr, jadi tulisan pertama berasal dari tempat lain. Di suatu tempat mungkin PROMPT_COMMAND, seperti yang ada di skrip startup default Debian untuk Bash. Ada sesuatu seperti ini di sana:

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

Ini menetapkan bahwa PROMPT_COMMANDjika berjalan xtermatau rxvt, yang seharusnya mendukung urutan pelarian itu.

ilkkachu
sumber
Tahukah Anda mengapa bash tampaknya membaca hal-hal satu demi satu, alih-alih membaca satu per satu? Juga, mengapa bash menulis "l" dan "s" ke stdout? Jika saya melakukan strace yang sama dengan cat, ada dua perbedaan: ia membaca input baris demi baris, dan sementara itu gema inputnya kembali ke stdout, saya melihat input dua kali (sekali ketika saya mengetik, dan satu kali ketika kucing menggema itu).
extremeaxe5
@ extremeaxe5, itu pada dasarnya karena Bash (atau lebih tepatnya perpustakaan readline) menangani semua pemrosesan baris perintah itu sendiri, daripada mengandalkan pemrosesan yang agak terbatas yang dilakukan oleh terminal. Itu harus mendapatkan input segera untuk memutuskan apa yang harus dilakukan ketika misalnya karakter TAB atau ^A(Ctrl-A) atau berbagai karakter khusus ditekan. Juga, ini mematikan gema terminal, sehingga ia dapat memutuskan apa yang akan dikeluarkan untuk setiap karakter input tertentu (sekali lagi, TAB biasanya tidak menghasilkan TAB.) Tidak catmelakukan itu. Jika ya, coba jalankan dash, yang tidak melakukan penanganan baris perintah.
ilkkachu
Sebenarnya, alasan panggilan Bash read()untuk hanya membaca satu byte pada suatu waktu adalah karena Bash tidak dapat membaca melewati baris baru. Baris baru mungkin menyebabkannya menjalankan program eksternal, yang mungkin juga membaca dari input yang sama. (Dan program yang harus dapat membaca karakter apapun setelah baris baru.) Jika tidak perlu peduli tentang itu, bisa menelepon read()dengan batas yang lebih besar, dan dengan terminal dalam mode mentah, itu akan tetap biasanya mendapatkan input satu karakter pada suatu waktu. (Tergantung pada seberapa cepat karakter input akan tiba dan bagaimana proses dijadwalkan.)
ilkkachu
Komentar kedua Anda tampaknya hanya benar karena Bash menangani sendiri command-line.
extremeaxe5
@ extremeaxe5, well, ya, saya berasumsi itu, karena itu adalah kasus umum. Tetapi, bahkan jika shell bergantung pada pengeditan baris terminal, waktunya masih bisa menjadi masalah. Jika dua baris dikirim secara berurutan (think paste data), dan sistem dimuat cukup sehingga shell tidak akan segera dijadwalkan (atau lebih buruk, shell dihentikan), maka read()buffer dengan yang lebih besar mungkin masih mengembalikan kedua baris di panggilan yang sama. Saya tidak berpikir ada jaminan yang read()akan selalu mengembalikan hanya satu baris dalam mode yang dimasak.
ilkkachu