나는 최근에 멋진 프롬프트를 인쇄하는 함수를 작성했으며 zuperPrompt
.zshrc에 PROMPT 변수를 설정하여 다음과 같이 함수를 호출했습니다.
setopt PROMPT_SUBST
PROMPT='$(zuperPrompt)'
같은 문제를 발견했습니다여기, 하지만 문제는 awk 명령에 사용되는 개행 이스케이프 문자에 있다고 생각합니다. 줄 바꿈 없이 전체 프롬프트를 시도했지만 동일한 결과를 얻었습니다.
기능은 다음과 같습니다.
zuperPrompt() {
# count chars in directory name
num_chars=1
if [ ! $(dirs) = '~' ]; then
let "num_chars=$(basename "`pwd`" | wc -m) - 1"
fi
# draw line
line="\n╭──"
for i in {1..$num_chars}; do
line+="─"
done
line+="──╯"
directory="%F{8}%K{8}%B%F{blue}%1~%k%F{8}"
arrows="%B%F{magenta}❯%B%F{yellow}❯%F{cyan}❯ "
row1="\n%B%F{green}◆ "$directory"─╮"
row2=$line
row3="\n╰─ "$arrows
print -P $row1$row2$row3
}
입력 후 Tab 키를 누른 결과입니다.python3
여기서 무슨 일이 일어나고 있는지 잘 모르겠습니다. 누구든지 도움을 주실 수 있다면 미리 감사드립니다!
고쳐 쓰다 내 웹 브라우저에서 이 구문 강조 표시를 본 후 $row3이 다른 색상과 다르다는 것을 알았습니다. $row1$row2를 사용하여 코드를 실행했는데 제대로 작동하고 커서가 움직이지 않습니다. 거기에서 무슨 일이 일어나고 있는지 아는 사람 있나요?
업데이트 2 최종 print 문에서 -P 옵션을 제거했는데 세 줄 모두에서 작동했습니다.
답변1
셸의 기본 프롬프트 문자열은 zsh
또는 변수에 할당된 문자열입니다. 여기에는 문자열을 눈에 보이는 프롬프트로 동적으로 확장하는 방법을 쉘에 알려주는 다양한 이스케이프 문자와 코드가 포함될 수 있습니다.PROMPT
PS1
print -rP -- $PROMPT
프롬프트 문자열을 직접 사용하거나 확장할 수 있습니다 print -rP -- $PS1
.
PROMPT
그러나 확장된 문자열 에 문자열을 할당하면 print -P
이스케이프 코드는 프롬프트 문자열의 어느 부분이 표시되고 프롬프트 문자열의 어느 부분이 색상을 변경하는 터미널 코드에만 사용되는지를 쉘에 알려줍니다. shell 힌트의 크기를 장기간 추적할 수 없습니다. 이로 인해 설명하는 문제 유형이 발생합니다. 또 다른 일반적인 문제는 긴 명령의 줄 바꿈이 엉망이 된다는 것입니다.
print -P
따라서 나중에 에 할당할 문자열을 생성하는 데 를 사용하는 대신 "원시"를 출력하는 데 사용하세요 PROMPT
.print -r
이는 설명된 것과 동일한 문제입니다.zsh 프롬프트가 올바르게 이스케이프되지 않았습니다., 그러나 약간 다른 이유가 있습니다(인쇄되지 않는 문자에 대한 정보를 추가하지 않고 프롬프트 문자열에서 인쇄되지 않는 문자에 대한 유용한 정보를 제거합니다).
조금 당황스러운 코드
num_chars=1
if [ ! $(dirs) = '~' ]; then
let "num_chars=$(basename "`pwd`" | wc -m) - 1"
fi
한줄로 작성 가능
num_chars=$( print -n -P '%1~' | wc -m )
( %1~
는 현재 디렉터리 이름에 대한 프롬프트 코드이며 나중에 프롬프트에 문자열을 실제로 포함하는 데 사용할 것입니다.)
루프도 있어요
line="\n╭──"
for i in {1..$num_chars}; do
line+="─"
done
line+="──╯"
다음과 같이 쓸 수 있다
printf -v line '\n╭──%*s──╯' $num_chars ''
line=${line// /─}
하지만 이를 다음과 같이 결합할 수 있습니다.
print -v wd -n -P '%1~'
printf -v line '\n╭──%*s──╯' $#wd ''
line=${line// /─}
이것은 추악한 명령 대체를 제거하고 wc
.
답변2
이 promptsubst
옵션을 활성화하면 다음을 수행할 수 있습니다.매개변수 확장,명령 대체그리고산술 확장프롬프트가 확장되면 확장되므로 가질 수 있지만 PS1='$(command-that-generates-PS1-dynamically)'
확장 결과는 여전히 %~
/ 가 %m
확장되는 프롬프트 문자열입니다.
그래서 당신이 하고 싶은 마지막 일은 command-that-generates-PS1-dynamically
전화를 거는 것 입니다 print -P
. 그 반대여야 합니다. command-that-generates-PS1-dynamically
출력을 문자 그대로 표시하려면 ( 켜져 있는 경우 %
에도 ) 이스케이프 처리되었는지 확인하고 커서 를 이동하지 않는 모든 제어 시퀀스가 에 포함되어 있는지 확인해야 합니다 .!
promptbang
%{
%}
$PS1
모든 이스케이프가 제공할 수 있는 텍스트 외에도 zsh-y에 가까운 또 다른 접근 방식 %x
은 동적 텍스트를 설정하지 않고 $promptsubst
후크에서 동적 텍스트를 생성하여 참조하는 파일에 저장하는 것입니다 precmd
.$psvar
%1v
%2v
$PS1
따라서 대신:
command-that-generates-PS1-dynamically() {
local some_dynamic_data=$(some-cmd)
print -r -- "%F{red}${some_dynamic_data//\%/%%}%f$ "
}
set -o promptsubst
PS1='$(command-that-generates-PS1-dynamically)'
( ${some_dynamic_data//\%/%%}
s 이스케이프만 담당하며 포함된 시퀀스를 %
처리하거나 이스케이프하지는 않습니다 .)!
$some_dynamic_data
하다:
prompt-hook() {
psvar[1]=$(some-cmd)
}
precmd_function+=(prompt-hook)
PS1='%F{red}%1v%f$ '
이는 또한 서브셸을 분기하는 수고를 덜어주고( prompt-hook
프롬프트 확장에서 일부 데이터를 유지할 수도 있음) %1v
확장(인쇄 가능한 텍스트용)은 인쇄할 수 없는 문자를 처리하거나 변환하여 표시되도록 합니다.
귀하의 경우에는 다음과 같이 보일 수 있습니다
display_width() REPLY=$(($#1 * 3 - ${#${(ml[$#1 * 2])1}}))
prompt-hook() {
display_width ${(%):-%1~}
psvar[1]=${(l[$REPLY][─])}
}
precmd_functions+=(prompt-hook)
() {
local directory='%F{8}%K{8}%F{blue}%1~%k%F{8}'
local arrows='%B%F{magenta}❯%F{yellow}❯%F{cyan}❯'
local line=$'─╮\n╭──%1v──╯\n╰─'
PS1=$'\n'"%B%F{green}◆ ${directory}${line} ${arrows}%b%f "
}
여기서는 $PS1
정적입니다(필요하지 않습니다 $promptsubst
. 여기서는 가독성을 높이기 위해 임시 섹션을 사용하고 있습니다). 그러나 동적 사용자 정의 섹션(확장된 표시 너비와 길이가 동일한 라인 섹션 %1~
)이 후크의 일부로 생성됩니다 precmd
.
이 프로세스 중에는 하위 쉘이 포크되지 않는다는 것을 알 수 있습니다.
바라보다이 답변도착하다문자열의 표시 너비를 가져옵니다.문자열의 표시 너비를 계산하는 함수에 대한 세부 정보 display_width
(반드시 문자 수와 같을 필요는 없음)