나는 최근에 이스케이프 시퀀스를 조사해 왔고 그것이 무엇을 할 수 있는지에 놀랐습니다. X11 창을 이동하는 데 에도 xterm
사용할 수 있습니다(시도해 보세요 printf '\e[3;0;0t'
). 와!
터미널이 어떤 기능을 지원하는지 알아내는 가장 일반적인 방법은 데이터베이스를 사용하는 것 같습니다. 이것이 바로 ncurses
이스케이프 시퀀스에 의존하는 응용 프로그램의 99.9%가 이를 사용하는 것입니다.
Ncurses는 콘솔에서 지원되는 기능을 결정하기 위해 terminfo
쉘 환경 변수의 데이터베이스를 읽습니다. 셸의 환경 변수를 변경할 수 있으며 , 이렇게 하면 대부분의 응용 프로그램이 더 적은 기능을 사용하거나 오작동(실행 또는 설정을 시도한 후 )을 시작할 수 있습니다.TERM
TERM
nano
vim
TERM=""
일부 이스케이프 코드로 인해 터미널이 무언가를 보고하게 되는 것을 발견했습니다. 예를 들어, <ESC>[6n
터미널이 커서 위치를 보고하도록 합니다. ( printf '\e[6n'
)
- 유사한 보고 메커니즘을 사용하여 콘솔이 지원하는 기능을 보고하도록 하면 어떨까요?
각 콘솔은 기능을 값에 결합하는 대신 자체 기능을 광고하여 TERM
전체를 더욱 정확하고 안정적으로 만들 수 있습니다. 왜 이게 별거 아니지?
편집: 전에 물어봤어야 했던 것... 새로운 이스케이프 시퀀스를 만들고, konsole 및 gnome-terminal을 해킹하여 이를 지원하고 일부 스크립트에서 사용하고 싶습니다.
내가 실행 중인 콘솔이 이 기능을 지원하는지 확인하기 위해 콘솔을 쿼리하고 싶습니다. 권장되는 접근 방식은 무엇입니까?
답변1
생각만큼 간단하지 않습니다. xterms(예: VT100으로 시작하는 DEC VTxxx 터미널)에는 다양한 상황에 대한 많은 보고서가 있습니다.특징(인용하다XTerm 제어 순서). 가장 일반적으로 유용한 것은 터미널 유형을 알려주는 것입니다.
CSI Ps c Send Device Attributes (Primary DA).
모든 터미널에 이러한 유형의 응답이 있는 것은 아닙니다(Sun 하드웨어 콘솔에는전혀).
그러나 보고보다 더 많은 기능이 있습니다(예: 터미널이 실제로 UTF-8을 해석하는지 확인하는 방법: 허용되는 경로는 다음을 통해입니다.로케일환경 변수이므로 다른 제어 시퀀스/응답을 구축할 필요가 없습니다.
실제로 보고에 중점을 둔 일부 애플리케이션이 있지만(예:윔, 기능키의 실제 값, 사용된 색상 수를 확인하세요.DCS + p Pt ST
, 또는 심지어 커서 모양을 사용하여DCS $ q Pt ST
), 일부 개발자는 기능을 구현하는 것보다 주어진 보고서 응답을 반환하는 것이 더 간단하다고 생각하기 때문에 프로세스를 신뢰할 수 없습니다. 다양한 프로그램의 소스 코드를 읽어보면 누군가가 특정 버전의 xterm처럼 보이도록 응답을 사용자 정의한 흥미로운 특징을 발견할 수 있습니다.
답변2
터미널 에뮬레이터의 이스케이프 시퀀스를 쿼리하는 방법은 다음과 같습니다.
요약: 비동기 특성으로 인해 애플리케이션에서는 이를 처리하는 데 문제가 있습니다. (케이크 조각인 터미널 에뮬레이터를 통하지 않습니다.)
먼저 단순화를 위해 모든 터미널 에뮬레이터가 이러한 모든 쿼리에 대한 응답을 보내는 것이 보장된다고 가정해 보겠습니다. (이렇게 하려면 쿼리에 일회성 이스케이프 시퀀스가 아닌 잘 정의된 공통 구조가 있어야 하지만 그런 것은 아닌 것 같습니다.)
간단한 유틸리티(예: "ls")와 보다 복잡한 전체 화면 응용 프로그램(예: "mc" 또는 "vim")을 마음 속에 설계하고 구현하고 어떤 문제에 직면하는지 살펴보겠습니다.
Unix의 표준 기능은 이전 명령이 실행되는 동안 미리 다음 명령을 입력할 수 있다는 것입니다(예를 들어 "sleep 10" Enter를 입력한 다음 "mc" Enter를 입력하고 F5를 누르면 약 10초 후에 , "mc ”를 누르면 복사 대화 상자가 열립니다.) 사람들은 이 기능을 좋아할 수도 있고 좋아하지 않을 수도 있지만 최소한 동작은 애플리케이션 전체에서 일관되어야 합니다. 이는 터미널 에뮬레이터에 동적으로 쿼리하지 않는 애플리케이션의 동작입니다. 또한 "mc"는 시작 시 이와 같은 쿼리 이스케이프 시퀀스를 사용하여 해당 기능이 무엇인지 알아낸다고 가정합니다. 이제 mc는 응답하기 전에 F5 이스케이프 시퀀스를 수신합니다. 이를 무시하거나(이 경우 동작이 애플리케이션의 나머지 부분과 일치하지 않음) 어딘가에 저장하고 쿼리에 대한 응답이 도착할 때까지 기다린 다음 이 F5를 처리해야 할 수 있습니다. 이를 위해서는 더 이상 각 구성요소가 stdin에서 직접 읽고 처리하도록 할 수 없으며, 그 사이에 일부 래핑 레이어가 필요합니다. 실행 가능하지만 구현하는 데 상당한 노력이 필요합니다(이 표준 없이 이미 구현된 프로젝트는 물론이고 처음부터 시작하는 프로젝트의 경우에도 필요한 추가 작업이 분명하므로 리팩토링을 많이 해야 합니다.)
어떤 이유로든 작동 중에 터미널을 쿼리해야 하는 경우 유사한 상황이 발생합니다. 그러면 응답이 도착하기 전에 중간에 있는 "일반" 문자를 버리는 것은 절대 용납될 수 없습니다.
이제 쉘 스크립트 자체가 C나 유사한 언어로 작성된 유틸리티 프로그램이 아니라 사용자로부터(표준 입력에서) 이 응답과 기타 일반 입력을 읽어야 한다고 상상해 보십시오. 어떻게 가고 싶나요? 이러한 응답을 처리하는 코드의 각 "읽기"에 대해 종결자를 수동으로 개행 또는 응답 이스케이프 시퀀스의 끝으로 설정하고 나머지에서 이스케이프 시퀀스를 찾아 제거하시겠습니까? 사용자 입력이 아직 있는 동안 화면에 이스케이프 시퀀스 응답이 표시되지 않도록 하려면 어떻게 해야 합니까? 사용자 데이터를 기대하는 후속 "정규" "읽기" 명령에 대한 이스케이프 시퀀스 응답을 기대할 때 수신하는 일반 입력을 어떻게 "공급"합니까? 어떻게 해야 할지 모르겠지만, 가능하다고 해도 참을 수 없을 정도로 복잡하고 지루하며 오류가 발생하기 쉽습니다. 합리적으로 달성 가능하고 안정적으로 작동한다고 생각되는 유일한 접근 방식은 자동 완성 문자(예기치 않은 동작 유발)를 제거하고 스크립트가 시작될 때 이러한 응답 이스케이프 시퀀스만 처리하는 것입니다.
루프 내의 쉘 스크립트에서 간단한 유틸리티(예: "ls")가 사용되는 경우 터미널 에뮬레이터와 애플리케이션 간의 왕복 시간이 상당히 길어질 수 있습니다. 단일 코어 시스템에서는 두 응용 프로그램(유틸리티와 터미널 에뮬레이터) 사이의 컨텍스트 전환이 필요하지만 어쨌든 발생했을 fork()+execve()+friends에 비하면 그다지 큰 문제는 아닐 것입니다. 이런. 멀티 코어 시스템에서는 이것이 필요하지 않을 것 같지만 자세한 내용은 확실하지 않습니다. 그러나 실제 네트워크 트래픽이 관련되면 비용(지연 시간)이 상당히 커질 수 있습니다.
응답을 읽지 않고 애플리케이션이 종료되는 드문 경우(예: 충돌 또는 종료), 입력을 시작하는 다음 명령에 응답이 나타납니다. 바이너리 파일을 분류했습니다).
이제 미래의 이스케이프 시퀀스를 포함하여 특정 쿼리 이스케이프 시퀀스를 인식하지 못하는(또는 단순히 응답하지 않도록 선택하는) 일부 터미널 에뮬레이터가 있다고 가정해 보겠습니다. 이것이 현재 상황입니다. 이것은 이전의 모든 요점을 훨씬 더 어렵게 만듭니다. 이 경우 애플리케이션이 정지될 위험이 없으므로 시간 초과가 필요합니다.
답변을 받으려면 얼마나 기다려야 합니까? 임의의 시간 초과를 보상하는 방법은 무엇입니까? 네트워크 특성(예: 로컬 터미널 에뮬레이터, 이웃 건물에 대한 SSH, 세계 반대편에 대한 SSH)을 기반으로 이 시간 초과를 조정합니까(그리고 어떻게)합니까?
SSH 지연 등으로 인해 응답이 제 시간에 도착하지 않으면 어떻게 되나요? 애플리케이션이 사용자에게 표시되는 성능 저하 모드로 유지되나요?
애플리케이션이 포기한 것보다 응답이 늦으면 어떻게 되나요? 응답이 전혀 예상하지 못한 곳에 도착하더라도 이러한 응답에 대해 애플리케이션을 준비해야 할 수도 있습니다. (예를 들어, 쉘 스크립트에서는 처음에 일부 상태를 쿼리하고 시간 초과 응답을 기다리지만 이후의 각 "읽기"는 이 지연된 응답으로 오염되도록 준비해야 합니다.)
시간 초과로 인해 왕복 시간이 많이 추가되며, 커널 컨텍스트 전환이나 네트워크 대기 시간보다 훨씬 길어질 수 있습니다. 이러한 명령을 셸 스크립트 루프에 넣는 것은 절대 견딜 수 없는 일이지만 대화형 응용 프로그램의 유용성에 심각한 부정적인 영향을 미칠 수도 있습니다.
제가 생각하기에 실행 가능한 대안을 제시하는 것은 이 답변의 범위를 벗어납니다. TERM 변수의 설계에는 많은 제한 사항이 있으므로 여기서는 자세히 설명하지 않겠습니다. 쿼리 기능(커서 위치와 같은 현재 속성이 아닌 터미널 에뮬레이터에 정적)의 경우 실제 동작을 설명하는 TERMCAP 방향으로 시작할 것입니다. 로컬 파일을 가리키고 지금처럼 TERM이라고 불릴 수도 있지만 ssh와 같은 유틸리티는 .Xauthority와 비슷한 방식으로 실제 내용을 원격 사이트로 전달하고 해당 파일에 TERM을 가리키는 역할을 합니다. 완전히 다른 접근 방식은 터미널 에뮬레이터와의 메타 통신을 위해 네 번째 표준 파일 설명자를 사용하는 것입니다.