XTerm을 시작하고 하단에 메시지를 표시하는 방법은 무엇입니까?

XTerm을 시작하고 하단에 메시지를 표시하는 방법은 무엇입니까?

XTerm을 시작하면 터미널의 첫 번째 줄에서 프롬프트가 시작됩니다. 명령을 실행할 때 프롬프트는 맨 아래에 도달할 때까지 아래로 이동하고 그 이후에는 그대로 유지됩니다(심지어 그렇지 않거나 Shift마우스 Page Down가 이를 변경할 수 있음). 최종 수명주기의 시작을 "특별"하게 만드는 대신프롬프트는 항상 터미널 하단에 있어야 합니다.제가 가지고 있는 점 참고해주세요여러 줄 프롬프트.

물론 이전과 마찬가지로 작동해야 합니다(크기 조정 가능, 스크롤 가능, 출력에서 ​​불필요한 줄 바꿈 없음, 출력이 이상하게 사라지지 않음). 따라서 이와 PROMPT_COMMAND='echo;echo;...'유사한 것은 옵션이 아닙니다. 이상적으로 솔루션은 쉘에 특정하지 않아야 합니다.

편집하다:이것현재 솔루션간단한 경우를 처리할 때 몇 가지 문제가 있습니다.

  • 그것은배쉬 특정. 이상적인 솔루션은 다른 셸로 이식 가능해야 합니다.
  • 그것다른 프로세스에 의해 수정되면 실패합니다.PS1. 예를 들어 virtualenv가 있습니다 (virtualenv).시작PS1그리고 항상 스크롤 없이 볼 수 있는 부분 위에서 사라집니다 .
  • Ctrll-지금삭제역사의 마지막 페이지.

XTerm을 분기하는 것 외에 이러한 문제를 피할 수 있는 방법이 있습니까?

답변1

사용하는 경우 bash다음이 트릭을 수행해야 합니다.

TOLASTLINE=$(tput cup "$LINES")
PS1="\[$TOLASTLINE\]$PS1"

tput또는 ( 각 프롬프트 전에 명령을 실행하지만 터미널 창 크기를 조정한 후에는 덜 효율적입니다 ):

PS1='\[$(tput cup "$LINES")\]'$PS1

종료 코드가 변경되는 것을 방지하려면 tput명시적으로 저장하고 재설정하면 됩니다.

PS1='\[$(retval=$?;tput cup "$LINES";exit $retval)\]'$PS1

이 변수는 로컬 변수이므로 셸에서 정의한 변수에는 retval영향을 주지 않습니다 .retval

대부분의 터미널 cup기능은 동일 하므로 \e[y;xH하드코딩할 수도 있습니다.

PS1='\[\e[$LINES;1H\]'$PS1

나중에 PS1을 재설정하는 일이 없도록 하려면 이 PROMPT_COMMAND변수를 사용할 수도 있습니다. 설정되면 명령으로 실행됩니다.앞으로출력 프롬프트. 그래서 이런 효과도 얻을 수 있습니다

PROMPT_COMMAND='(retval=$?;tput cup "$LINES";exit $retval)'

물론 재설정은 PS1여기에 영향을 미치지 않지만 일부 다른 소프트웨어도 변경될 수 있습니다 PROMPT_COMMAND.

답변2

이전 답변을 약간 단순화하여 실행하기가 더 쉽다는 것을 알았습니다.

tput cup $LINES

.bashrc또는 의 시작 부분에 .zshrc. 그것은 단지 일을 끝내는 것입니다.

이점:

  • 쉘을 시작하면 한 번만 인쇄됩니다.

결점:

  • ^L을 사용하여 화면을 지우면 인쇄되지 않으며 별칭도 도움이 clear되지 않습니다 clear; tput ....
  • 터미널 크기 조정 시 다른 위치로 이동하라는 메시지 표시

답변3

나는 한동안 \[\033[1000H\]솔루션에 @Thomas Dickey의 포함을 사용해 왔으며 , 종종 발생하는 여러 줄 명령의 vi 모드 편집이 중단되기 때문에 그 이후로 계속 실망했습니다.PS1

지난 몇 달 동안 문제를 여러 번 조사한 후 마침내 문제를 방지하는 작은 변경 사항을 발견했습니다. ANSI 이스케이프 시퀀스는 에 포함되어서는 안 되지만 from (in 또는 다른 구성 파일) PS1에 직접 인쇄되어야 합니다 .stdoutPROMPT_COMMAND~/.bashrc

PROMPT_COMMAND=prompt

prompt() {
    r="${?}"
    printf '\033[1000H'
    return "${r}"
}

printfreturn내장 함수(참고자료 참조 builtins(1))이므로 프로세스가 생성되지 않습니다. 이전 명령의 종료 상태는 에 저장되며 r, Clear를 사용하고 printf, Restore를 사용합니다 return. 이미 사용하고 계시 다면 PROMPT_COMMAND간단히 추가하시면 됩니다.

clear-screen이 솔루션은 여러 줄 명령에 작동하지만 두 가지 작은 제한 사항이 있습니다. 첫째, readline 명령(기본적으로 바인딩됨)을 ​​사용하여 화면을 지우면 Control-L프롬프트가 화면 상단으로 이동하고, 둘째, 새 터미널을 추가하면 계속 유지됩니다. 원래 위치에 프롬프트가 표시됩니다. 두 경우 모두 프롬프트가 인쇄되면(예: Enter 누르기) 화면 하단으로 돌아갑니다(bash가 실행 중인 동안). 이는 @phil pirozhkov가 제안한 PROMPT_COMMAND단일 또는 유사한 열기보다 개선되었습니다.tput cup 1000~/.bashrc

추가 조사에 따르면 clear프로그램은 터미널 이스케이프 시퀀스( clear | xxd또는 유사한 경로를 통해 관찰 가능)를 직접 인쇄하여 Bash를 다시 실행하게 되므로 제대로 작동하는 것으로 나타났습니다 PROMPT_COMMAND. clear-screen반면에 readline 명령을 사용하면 readline(Bash에서 사용)이 화면 지우기 요청을 가로채서 다시 실행하지 않고 이전 프롬프트를 간단히 다시 표시할 수 있습니다 PROMPT_COMMAND. \033[1000H이스케이프 시퀀스는 더 이상 프롬프트 자체에 없지만 running 의 부작용이므로 프롬프트 PROMPT_COMMAND는 원래 위치에 유지됩니다.

가장 일반적인 해결책은 PROMPT_COMMAND터미널 크기가 조정되거나 clear-screen실행될 때 실행되도록 Bash를 패치하는 것입니다. 그러나 저는 보다 간단한 해결책을 선택했습니다. 즉, 직접 인쇄하도록 명령을 패치하는 것입니다 clear-screen. 명확히 말하면 Bash의 기본 소스 구성에는 자체 복사본이 포함되어 있지만 내 시스템(Gentoo Linux)에서 기본 Bash 구성은 system을 사용하는 것이며 패치 시스템은 이를 사용하는 다른 프로그램에서도 작동합니다.\033[1000Hstdoutreadlinereadlinereadline

어쨌든 가장 간단한 해결책은 Bash를 다운로드하고 patch -p1Bash 소스 디렉터리에 다음 패치를 적용하는 것입니다.

--- a/lib/readline/display.c    2021-12-20 10:00:33.370809888 +0000
+++ b/lib/readline/display.c    2021-12-20 09:59:14.920808045 +0000
@@ -3186,11 +3186,17 @@
   ScreenClear ();
   ScreenSetCursor (0, 0);
 #else
+  static char const cup[]={'\033', '[', '1', '0', '0', '0', 'H'};
+  size_t i;
+
   if (_rl_term_clrpag)
     {
       tputs (_rl_term_clrpag, 1, _rl_output_character_function);
       if (clrscr && _rl_term_clrscroll)
    tputs (_rl_term_clrscroll, 1, _rl_output_character_function);
+
+      for (i=0; i < sizeof(cup); i++)
+   putc (cup[i], rl_outstream);
     }
   else
     rl_crlf ();

이 패치는 Bash 5.1에 포함된 readline 8.1에 적용됩니다. 패치는 함수 clrscr에 대한 인수를 포함하지 않기 때문에 readline 8.0에서는 작동하지 않지만 _rl_clear_screen이에 대한 패치를 변경하는 것은 간단합니다.

이 작업을 수행하는 동안 저는 또 다른 개선 사항을 결정했습니다. clear-screen화면 내용을 삭제하는 명령을 사용하는 대신 나중에 터미널의 스크롤백 버퍼에서 해당 내용에 액세스할 수 있도록 내용을 위로 스크롤하고 싶었습니다. 이를 달성하기 위해 이전 패치 위에 다음 패치를 적용할 수 있습니다.

--- a/lib/readline/display.c    2021-12-20 23:48:23.253322032 +0000
+++ b/lib/readline/display.c    2021-12-20 23:48:41.813321757 +0000
@@ -3191,6 +3191,15 @@
 
   if (_rl_term_clrpag)
     {
+      if (!clrscr)
+      {
+   for (i=0; i < sizeof(cup); i++)
+     putc (cup[i], rl_outstream);
+
+   for (i=0; i < _rl_screenheight; i++)
+     putc ('\n', rl_outstream);
+      }
+
       tputs (_rl_term_clrpag, 1, _rl_output_character_function);
       if (clrscr && _rl_term_clrscroll)
    tputs (_rl_term_clrscroll, 1, _rl_output_character_function);

이를 통해 터미널과 셸이 마침내 내가 원하는 방식으로 정확히 작동합니다. 남은 유일한 문제는 첫 번째 명령을 입력하기 전에 터미널 크기를 늘리면 첫 번째 명령을 입력할 때까지 프롬프트가 그대로 유지된다는 것입니다. 그러나 이는 본질적으로 문제가 되지 않습니다. 나는 여기에 두 패치를 모두 작성하여 CC0 공개 도메인 공개에 따라 공개 도메인에 릴리스합니다(누군가가 이를 향후 프로젝트에 통합하려는 경우).


편집: @Toby Speight의 요청에 따라 다른 터미널에 적용할 수 있습니다. 의 경우 PROMPT_COMMAND의 출력을 tput cup 1000변수에 저장하고 대신 ~/.bashrc인쇄합니다.\033[1000H@토마스 디키의 답변.

패치의 경우 \033[1000H속도를 위해 이스케이프 시퀀스를 직접 삽입했습니다(그래서 커서 위치를 조정하기 위해 하나의 이스케이프 시퀀스만 인쇄됨). 왜냐하면 나만 사용하기 때문입니다. 동일한 파일에 있는 다른 readline 함수를 사용하면 보다 이식성이 뛰어난 솔루션을 쉽게 얻을 수 있습니다(display.c),특히_rl_move_vert기능. 불행하게도 Bash/readline은 현재 수직 커서 위치(다음 위치에 저장됨)를 정확히 알지 못하는 것 같습니다._rl_last_v_pos)대부분의 상황에서.

내가 아는 한, 시작 시 현재 커서 위치가 쿼리되지 않습니다. 즉, 화면에서 프롬프트가 시작되는 위치를 알 수 없다는 의미입니다. 함수 _rl_move_vert자체가 단순히 아래로 이동합니다.putc ('\n', rl_outstream)즉, 커서가 이미 터미널의 마지막 줄에 있을 때 "아래로 이동"하면 실제로 터미널 버퍼를 스크롤합니다.

이것_rl_clear_screen기능또한 전혀 업데이트되지 않습니다 _rl_last_v_pos. 즉, 명령을 사용하여 화면을 지운 후 커서가 어디에 있는지 알 수 없지만 의 clear-screen명령을 사용하여 액세스할 수 있습니다 _rl_clear_screen. readline은 아마도 이스케이프 시퀀스를 사용하도록 확장될 수 있습니다. 선택 사용 _rl_term_clrpag(화면 지우기) 및 기타 관련 이스케이프 시퀀스와 유사하게 커서 위치를 이동합니다. 그러나 이것이 어떻게 작동하는지, 그리고 이러한 이스케이프 시퀀스가 ​​어디서 초기화되는지는 확실하지 않습니다.

어쨌든, 나는 _rl_move_vert여전히 작동하는 기능을 갖춘 새 패치를 작성했습니다. 뒤로 스크롤하면 항상 전체 페이지를 스크롤하는 것이 아니라 필요한 양(바람직할 수 있음)만 스크롤한다는 점에서 이전 패치와 다릅니다. 적어도 제 생각에는 다음과 같습니다.

--- a/lib/readline/display.c    2021-12-21 09:57:55.932774006 +0000
+++ b/lib/readline/display.c    2021-12-21 09:19:59.109474783 +0000
@@ -3186,11 +3186,25 @@
   ScreenClear ();
   ScreenSetCursor (0, 0);
 #else
+  int i;
+
   if (_rl_term_clrpag)
     {
+      if (!clrscr)
+      {
+   i = _rl_screenheight-_rl_last_v_pos;
+   _rl_move_vert(_rl_screenheight);
+
+   for (; i < _rl_screenheight; i++)
+     putc ('\n', rl_outstream);
+      }
+
       tputs (_rl_term_clrpag, 1, _rl_output_character_function);
       if (clrscr && _rl_term_clrscroll)
    tputs (_rl_term_clrscroll, 1, _rl_output_character_function);
+
+      _rl_last_v_pos = 0;
+      _rl_move_vert(_rl_screenheight);
     }
   else
     rl_crlf ();

답변4

사용된 답변은 $LINES불필요하게 이식성이 없습니다. 완료됨resize, 간단하게 물어보시면 됩니다xterm위치를 임의로 큰 줄 번호로 설정합니다. 예를 들어 다음과 같습니다.

tput cup 9999 0

(창의 행이 10,000개 미만이라고 가정하면 무시하세요.롤백).

창 크기가 조정될 때 문자열은 변경되지 않으므로 한 번 계산한 다음 프롬프트 문자열에 상수로 붙여넣을 수 있습니다. 예:

TPUT_END=$(tput cup 9999 0)

그 다음에

PS1="${TPUT_END} myprompt: "

귀하의 선호도에 따라.

다른 프로세스 수정의 경우 원하는 대로 표시되는지 확인하려면 이러한 변경 후에 다시 계산 PS1해야 합니다 . PS1하지만 질문에 지적할 만한 세부정보가 충분하지 않습니다.어디변화시키다.

마지막으로 bash의 가정으로 인해 탭 완성 동작은 이러한 유형의 변경과 일치하지 않습니다.

관련 정보