Баш намагається написати два підказки оболонки?


11

Я дивлюся на напружений вихід запущеного процесу bash, підключеного до терміналу, для навчальних цілей.

Мій процес bash має PID 2883.

Я набираю

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

У термінал. Тоді я переходжу до свого баш-процесу і маю таку взаємодію:

[OP@localhost ~]$ ls

Дивлячись на вихід, я бачу

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

Я заплутався в останніх двох рядках. Здається, Bash намагається написати два підказки оболонки? Що тут відбувається?

Відповіді:


24

<ESC>]0;Послідовність (показано \33]0;на трасування) є послідовністю виходу , щоб встановити заголовок вікна терміналу. Він закінчується символом BEL ( \7), тому перший writeвстановлює назву вікна. Друга друкує власне підказку. Зауважте, що навіть крім послідовності втечі, вони не зовсім однакові. Підказка містить оточуючі, [..]а заголовок вікна цього немає.

Ми також можемо побачити, що перша запис йде у stdout (fd 1, перший аргумент до write()), а друга в stderr. Bash друкує підказку на stderr, тому перше написання надходить з іншого місця. Це десь, мабуть PROMPT_COMMAND, як у сценаріях запуску Debian за замовчуванням для Bash. Тут є щось подібне:

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

Він встановлює, що PROMPT_COMMANDякщо працює xtermабо rxvt, який повинен підтримувати цю послідовність втечі.


Чи знаєте ви, чому баш, здається, читає речі за характером, а не читає в рядку за раз? Крім того, чому bash пише "l" і "s" на stdout? Якщо я роблю подібний пробіл cat, є дві відмінності: він читає вхідний рядок за рядком, і, хоча він повторює свій вхід назад до stdout, я бачу вхід двічі (один раз, коли я набираю, і один раз, коли кішка повторює його).
extremeaxe5

@ extremeaxe5, це в основному тому, що Bash (а точніше бібліотека readline) обробляє всю обробку командного рядка сам, замість того, щоб покладатися на досить обмежену обробку, виконану терміналом. Він повинен негайно отримати вхід, щоб вирішити, що робити, наприклад, якщо ^Aнатиснути символ TAB або (Ctrl-A) або різні спеціальні символи. Крім того, він відключає відлуння терміналу, щоб він міг вирішити, що виводити для кожного конкретного вхідного символу (знову ж таки, TAB зазвичай не видає TAB.) catНічого з цього не робить. Якщо ви це зробите, спробуйте запустити dash, що не виконує жодної обробки командного рядка.
ilkkachu

Насправді, причина, по якій Bash закликає read()читати лише один байт за один раз, полягає в тому, що він не може прочитати новий рядок. Новий рядок може призвести до запуску зовнішньої програми, яка також може читатися з того ж входу. (І ця програма повинна мати можливість читати будь-які символи після символу нового рядка.) Якщо це не потрібно піклуватися про те, що можна назвати read()з великим лімітом і з терміналом в нестандартному режимі, він все одно зазвичай отримує вхід по одному персонажу. (Було б
залежно

Ваш другий коментар видається справжнім лише тому, що Bash сам працює з командним рядком.
extremeaxe5

@ extremeaxe5, ну, так, я припускав це, оскільки це все одно звичайний випадок. Але, навіть якщо оболонка спиралася на редагування лінії терміналу, терміни все ще можуть бути проблемою. Якщо два рядки були надіслані швидкою послідовністю (продумайте вставку даних), і система була завантажена достатньо, щоб оболонка не одразу була запланована (або що ще гірше, оболонка була зупинена), то read()з більшим буфером все одно можна повернути обидва рядки в той самий дзвінок. Я не думаю , що є гарантія того, що read()буде завжди повертати тільки один рядок в приготованому режимі.
ilkkachu
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.