Bash: Hiển thị trạng thái thoát trong dấu nhắc:


11
GREEN="\e[1;32m"
RED="\e[1;31m"
NONE="\e[m"

get_exit_status(){
   es=$?
   if [ $es -eq 0 ]
   then
       echo -e "${GREEN}${es}${NONE}"
   else
       echo -e "${RED}${es}${NONE}"
   fi
}

get_path(){
    #dummy function
    echo "PATH"
}

PROMPT_COMMAND='exitStatus=$(get_exit_status)'

Sau đây cho tôi thoát exitStatus chính xác nhưng các biến màu không được mở rộng:

PS1='${RED}\h $(get_path) ${exitStatus}${NONE} '

Tuy nhiên, cái bên dưới, cho tôi màu sắc nhưng trạng thái thoát không cập nhật:

PS1="${RED}\h $(get_path) ${exitStatus}${NONE} "

Cách đúng đắn để làm điều này là gì? Làm cách nào để khắc phục điều này để exitStatus và màu sắc đều hoạt động?

Câu trả lời:


8

Gilles đã xác định vấn đề chính của bạn, nhưng tôi muốn thử giải thích nó theo cách khác.

Bash diễn giải lời nhắc đặc biệt chỉ thoát trước khi mở rộng bất kỳ biến nào trong dấu nhắc. Điều này có nghĩa là việc sử dụng \emột biến được mở rộng từ dấu nhắc không hoạt động, mặc dù nó hoạt động trực tiếp PS1.

Ví dụ: điều này hoạt động như mong đợi và đưa ra văn bản màu đỏ:

PS1='\e[1;31m this is in red '

Nhưng điều này không, nó chỉ đặt một chữ đúng nghĩa \e:

RED='\e[1;31m'
PS1="$RED not in red "

Nếu bạn muốn lưu trữ các màu thoát trong các biến, bạn có thể sử dụng trích dẫn ANSI-C $'...'để đặt ký tự thoát theo nghĩa đen trong biến.

Để làm điều này, bạn có thể thay đổi định nghĩa của bạn GREEN, REDNONE, vì vậy giá trị của họ là dãy thoát thực tế.

GREEN=$'\033[1;32m'
RED=$'\033[1;31m'
NONE=$'\033[m'

Nếu bạn làm điều đó, lần đầu tiên của bạn PS1với dấu ngoặc đơn sẽ hoạt động:

PS1='${RED}\h $(get_path) ${exitStatus}${NONE} '

Tuy nhiên, sau đó bạn sẽ có một vấn đề thứ hai.

Hãy thử chạy nó, sau đó nhấn Up Arrow, sau đó Home, và con trỏ của bạn sẽ không quay trở lại điểm bắt đầu của dòng.

Để khắc phục điều đó, hãy thay đổi PS1để bao gồm \[\]xung quanh các chuỗi thoát màu, ví dụ:

PS1='\[${RED}\]\h $(get_path) $?\[${NONE}\] '

Bạn không thể sử dụng get_exit_statusđúng cách ở đây, vì đầu ra của nó chứa cả in (mã thoát) và các ký tự không in (mã màu) và không có cách nào để đánh dấu chính xác trong lời nhắc. Đặt \[...\]sẽ đánh dấu nó là không in đầy đủ, không đúng. Bạn sẽ phải thay đổi chức năng để nó chỉ in mã màu phù hợp và sau đó bao quanh nó \[...\]trong dấu nhắc.


\[\1, và \[\2. Những cái đó tương ứng với một số RL_PROMPT_{START,END}_IGNOREthứ của readline yêu cầu nó bỏ qua các byte khi đếm độ dài dấu nhắc trên màn hình. Xem danh sách.gnu.org/archive/html/orms-bash/2015-08/msg00027.html .
Arthur2e5

@ Arthur2e5 Ý bạn \]\2gì? Và bạn có nghĩa đó là lý do tại sao nó cần thiết ${exitStatus}? Quan điểm của tôi là ${exitStatus}không chứa các ký tự không in, vì vậy bash có thể xác định chính xác có bao nhiêu ký tự di chuyển dấu nhắc mà không có \[\]in \[${exitStatus}\].
Mikel

Vấn đề là nó có chứa một số - màu sắc. (Thoát
khỏi

@ Arthur2e5 Ew, tôi hoàn toàn bỏ lỡ điều đó. :) Tại sao bạn sẽ đặt màu sắc ... không bao giờ để tâm. :)
Mikel

1
"Bash đang gọi echo một cách hiệu quả trên PS1 của bạn chứ không phải echo -e" - đó là sai hoặc chỉ thiếu điểm. Bash không mở rộng xuyệc ngược-thoát như \e\033(và \[/ \], \u\h) từ dấu nhắc, nó chỉ làm như vậy trước khi mở rộng các biến. Vì vậy, PS1='\e[1;31m red'hoạt động, red='\e[1;31m'; PS1='$red red'không.
ilkkachu

3

Khi bạn chạy PS1='${RED}\h $(get_path) ${exitStatus}${NONE} ', PS1biến được đặt thành ${RED}\h $(get_path) ${exitStatus}${NONE}, trong đó chỉ \hlà một chuỗi thoát nhanh chóng. Sau khi các chuỗi nhắc được mở rộng (năng suất ${RED}darkstar $(get_path) ${exitStatus}${NONE}), shell thực hiện các mở rộng thông thường như mở rộng biến. Bạn nhận được một lời nhắc hiển thị trông giống như \e[1;31mdarkstar PATH 0\e[m. Không có gì trên đường đi mở rộng \echuỗi các nhân vật thoát thực tế.

Khi bạn chạy PS1="${RED}\h $(get_path) ${exitStatus}${NONE} ", PS1biến được đặt thành \e[1;31m\h PATH 0\e[m. Các biến RED, exitStatusNONEđược mở rộng tại thời điểm chuyển nhượng. Sau đó, nhanh chóng chứa ba trình tự thoát prompt ( \e, \h, và \emột lần nữa). Không có biến shell để mở rộng ở giai đoạn này.

Để xem màu sắc, bạn cần các biến màu để chứa các ký tự thoát thực tế. Bạn có thể làm theo cách này:

RED=$'\033[1;31m'
NONE=$'\033[m'
PS1='\[${RED}\]\h \w $?\[${NONE}\] '

$'…'mở rộng các chuỗi dấu gạch chéo ngược và một số chuỗi ký tự dấu gạch chéo ngược như \n, nhưng không bao gồm \e. Tôi đã thực hiện ba thay đổi khác cho lời nhắc của bạn:

  • Sử dụng \[…\]xung quanh các chuỗi không in như các lệnh thay đổi màu. Nếu không, màn hình của bạn sẽ bị cắt xén vì bash không thể tìm ra chiều rộng của dấu nhắc.
  • \w là một chuỗi thoát tích hợp để in thư mục hiện tại.
  • Bạn không cần bất cứ điều gì phức tạp để hiển thị $?trong lời nhắc nếu bạn không có PROMPT_COMMANDở vị trí đầu tiên.

Tôi nghĩ ý tưởng là làm cho dấu nhắc thành màu xanh thành công và màu đỏ cho thất bại.
mattdm

Vâng, PS1là sai, nhưng những lời khuyên để sử dụng $'...'cho REDGREENnên làm cho nó hoạt sử dụng dogbane của PS1.
Mikel

1

Thử:

PS1='`exitStatus=$?;if [ $exitStatus -eq 0 ];then echo "\['${GREEN}'\]";else echo "\['${RED}'\]";fi;echo "\h $(get_path) ${exitStatus}${NONE}"`'

1
Cảm ơn, điều này hoạt động, nhưng có cách nào để thực hiện điều này mà không phải nhúng câu lệnh if trong dấu nhắc không?
dogbane

1

Đây là cách tiếp cận tôi đã thực hiện, nó tránh sử dụng PROMPT_COMMAND.

# This function is called from a subshell in $PS1,
# to provide a colourised visual indicator of the exit status of the last run command
__COLOURISE_EXIT_STATUS() {
    # uncomment the next line for exit code output after each command, useful for debugging and testing
    #printf -- "\nexit code: $1\n" >&2
    [[ 0 == "$1" || 130 == "$1" ]] && printf -- "$GREEN" || printf -- "$RED"
}

Sau đó, tôi $PS1là như sau:

PS1='# ${debian_chroot:+($debian_chroot)}'"${GREEN}\u${YELLOW}@${DARK_YELLOW}\h${WHITE}:${LIGHT_BLUE}\w${WHITE}\n"'\[$(__COLOURISE_EXIT_STATUS $?)\]# \$'"\[${WHITE}\] "

1
Mặc dù nó không quan trọng trong trường hợp cụ thể này, vì giá trị duy nhất $?có thể có là một số nguyên, bạn thực sự nên sử dụng printf '%b' "$GREEN"thay thế. Ngoài ra, tránh sử dụng tên hàm có tiền tố __hoặc _vì chúng được sử dụng khi hoàn thành bash.
nyuszika7h

1

Ở đây bạn đi - Cái này hoạt động cho tôi (TM) trong Ubuntu và các Linux khác (Linuxen?).

Lý do cho việc phát hiện mã thoát $PS1là một máy chủ có $PROMPT_COMMANDcài đặt chỉ đọc trước khi đọc .bashrc.


0

Đối với PROMPT_COMMAND, nó sạch hơn để xác định một chức năng và sử dụng chức năng đó:

prompt_command() {
    # ...
}
PROMPT_COMMAND=prompt_command
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.