쉘 프롬프트에 명령이 제공되었는지 감지하는 방법

쉘 프롬프트에 명령이 제공되었는지 감지하는 방법

PROMPT_COMMAND나는 바로 이전에 명령 프롬프트에 제공된 내용에 응답하는 를 작성하고 싶습니다 . 예를 들어, 다음과 같이 광범위하고 정보가 풍부한 프롬프트와 간단하고 간결한 프롬프트 사이를 전환하려면:

mikemol@serenity ~ $ echo hi
hi
$ echo ho
ho
$ echo hum
hum
$
mikemol@serenity ~ $

값은 비어 있지 않은 경우에만 셸 기록에 추가되는 것으로 나타나므로 가장 최근 기록 항목이 비어 있는지 간단히 테스트할 수는 없습니다. set | grep some_command실행 후 실행하면 some_command아무 것도 제공되지 않으므로 해당 정보가 포함된 환경 변수가 나타나지 않습니다.

저는 주로 를 사용 bash하지만 POSIX 호환 솔루션과 기타 셸에 대해 궁금합니다.

답변1

나는 궁극적으로 PROMPT_COMMAND전혀 필요하지 않았습니다. 나에게 올바른 방향을 알려준 Christopher에게 감사드립니다.

대신 다음 파일을 고려해보세요 ps1.prompt.

${__cmdnbary[\#]+$(
    echo '\u@\h: \w' # Your fancy prompt goes here, with all the usual special characters available.
) }${__cmdnbary[\#]=}\$

그런 다음 이것을 내 PS1:

PS1=$(cat ps1.prompt)

(이렇게 할 필요는 없지만 일러스트레이션이나 편집에는 편리하다고 생각했습니다.)

그래서 우리는 다음을 봅니다:

mikemol@zoe:~$ echo hi
hi
mikemol@zoe:~$ echo ho
ho
mikemol@zoe:~$ echo hum
hum
mikemol@zoe:~$ 
mikemol@zoe:~$ PS1=$(cat ps1.prompt)
$ 
mikemol@zoe: ~ $ echo hi
hi
$ echo ho
ho
$ echo hum
hum
$ 
mikemol@zoe: ~ $ 

우리는 배열 핵을 사용하고 있습니다여기서 시연bash하지만 의 매개변수 대체를 사용하는 대신 이전 명령이 실행되지 않은 경우에만 트리거하도록 ${parameter:-word}사용합니다 .${parameter+word}

논리에서 이중 부정을 사용해야 하므로 이에 대한 설명이 필요합니다.

${__cmdnbary[\#]-word}${__cmdnbary[\#]=}작동 원리

원래 배열 해킹 데모에서는 해당 구성이 ${__cmdnbary[\#]-word}${__cmdnbary[\#]=}사용되었습니다. (명확성을 위해 $?로 대체했습니다 word). 매개변수 확장과 배열에 특별히 익숙하지 않다면(저는 익숙하지 않았습니다), 무슨 일이 일어나고 있는지 전혀 명확하지 않습니다.

첫째, 이해하다\#

매뉴얼:

\#이 명령의 명령 번호

...

명령 번호는 현재 셸 세션 동안 실행되는 명령 순서의 위치입니다.

이는 \#단지 변경 사항이 있음을 의미합니다.만약에 그리고 만약에명령이 실행됩니다. 사용자가 프롬프트에 빈 줄을 입력하면 명령이 실행되지 않으므로 \#변경되지 않습니다.

${__cmdnbary[#]=}에 빈 문자열 설정

${__cmdnbary[\#]=}매개변수 확장을 사용합니다. 다음으로 돌아가기매뉴얼:

${parameter:=word}

기본값 할당. 매개변수가 설정되지 않거나 null인 경우 단어의 확장이 매개변수에 할당됩니다. 그런 다음 매개변수의 값이 대체됩니다.

따라서 __cmdnbary[\#]가 설정되지 않거나 null인 경우 이 구문은 빈 문자열(우리의 경우 는 빈 문자열)을 할당 word하고 전체 구문은 출력에서 ​​동일한 빈 문자열로 대체됩니다.

__cmdnbary[\#]~ 할 것이다언제나#은 단조롭기 때문에 처음 볼 때 설정되지 않거나 null이어야 합니다. 항상 증가하거나 동일하게 유지됩니다. (즉, 반복될 때까지 2^31 또는 2^63 정도일 가능성이 높지만 다른 문제가 발생할 수 있습니다.우리가 거기 도착하기 전에. 내가 이 솔루션을 약간의 해킹이라고 설명하는 이유가 있습니다.)

조건부${__cmdnbary[\#]-word}

${__cmdnbary[\#]-word}또 다른 매개변수 확장입니다. 매뉴얼에서:

${parameter:-word}

기본값 사용. 매개변수가 설정되지 않거나 null이면 단어의 확장이 대체됩니다. 그렇지 않으면 매개변수 값이 대체됩니다.

따라서 배열 항목이 다음과 \#같다 면설정되지 않았거나 null, word그 자리에 사용됩니다. 우리는 그것을 확인할 때까지 __cmdnbary[\#](대체를 사용하여 ) 할당을 시도하지 않기 때문에 ${parameter:=word}처음으로 주어진 것을 확인할 때 \#배열에서 해당 위치가 설정되지 않은 것을 찾아야 합니다.

bash희소 배열을 사용합니다

C 스타일 배열에 익숙한 사람들을 위한 한 가지 설명입니다. bash실제로 사용희소 배열; 할당할 때까지무엇배열의 특정 위치로 이동하면 해당 위치가 설정 해제됩니다. 빈 문자열은 "null 또는 설정되지 않음"과 동일하지 않습니다.

대신 ${__cmdnbary[#]+word}${__cmdnbary[#]=}를 사용하는 이유

${__cmdnbary[\#]+word}${__cmdnbary[\#]=}및 ${__cmdnbary[#]-word}${__cmdnbary[#]=} look very siilar. The *only* thing we change between the two constructs can be found in the first portion; we use${parameter:+word} instead of${parameter:-word}`.

with 는 ${parameter:-word}null 이거나 설정되지 않은 word경우에만 표시됩니다 parameter. 우리의 경우에는아직\#배열의 위치를 ​​아직 설정합니다. 이는 증가한 경우에만 수행되지 않으며 방금 명령을 실행한 경우에만 발생합니다.

즉, 를 사용하면 다음과 같은 경우 ${parameter:-word}에만 발표할 것입니다.word아직우리가 원하는 것과 정확히 반대되는 명령을 실행했습니다. 그래서 ${parameter:-word}대신 사용합니다. 다시, 매뉴얼에서:

${parameter:+word}

대체 값 사용. 매개변수가 null이거나 설정되지 않은 경우 아무것도 대체되지 않으며, 그렇지 않으면 단어 확장이 대체됩니다.

(불행히도) 이해해야 할 이중 부정 논리가 더 많지만 여기까지입니다.

프롬프트 자체

전환 메커니즘에 대해 설명했지만 프롬프트 자체는 어떻습니까?

여기서는 $( ... )프롬프트의 내용을 담는 데 사용합니다. 주로 가독성에 대한 나 자신의 이익을 위해; 그렇게 할 필요는 없습니다. $( ... )일반적으로 변수 할당에 포함할 수 있는 항목으로 바꿀 수 있습니다 .

왜 해킹인가요?

희소 배열에 항목을 추가하는 방법을 기억하시나요? 우리는 해당 항목을 제거하지 않으므로 쉘 세션이 종료될 때까지 배열이 영원히 커집니다. 껍질이 새고 있습니다 PS1. 그리고 내가 아는 한, 그럴 방법은 없어설정되지 않음프롬프트의 변수 또는 배열 위치. 에서 시도해 볼 수는 있지만 $()작동하지 않는다는 것을 알게 될 것입니다. 서브셸 내부의 변수 네임스페이스에 대한 변경 사항은 서브셸이 분기된 공간에 적용되지 않습니다.

~할 것 같다mktmp의 초기 .bashrc, PS1할당 전 및 결과 파일의 정보를 사용해 보십시오 . 그런 다음 현재 상태를 \#거기에 저장한 것과 비교할 수 있지만 이제 프롬프트를 디스크 I/O에 종속되게 만들었습니다. 이는 긴급 상황에서 자신을 잠글 수 있는 좋은 방법입니다.

관련 정보