Sự khác biệt giữa PS1 và PROMPT_COMMAND là gì


108

Trong khi xem qua chuỗi tuyệt vời này, tôi nhận thấy rằng một số ví dụ sử dụng

PS1="Blah Blah Blah"

và một số sử dụng

PROMPT_COMMAND="Blah Blah Blah"

(và một số sử dụng cả hai) khi đặt lời nhắc trong bash shell. Sự khác biệt giữa hai là gì? Một tìm kiếm SO và thậm chí một chút tìm kiếm trên google rộng hơn không mang lại cho tôi kết quả, vì vậy ngay cả một liên kết đến đúng nơi để tìm kiếm câu trả lời cũng sẽ được đánh giá cao.

Câu trả lời:



67

PROMPT_COMMAND có thể chứa các câu lệnh bash thông thường trong khi biến PS1 cũng có thể chứa các ký tự đặc biệt, chẳng hạn như '\ h' cho tên máy chủ, trong biến.

Ví dụ ở đây là lời nhắc bash của tôi sử dụng cả PROMPT_COMMAND và PS1. Mã bash trong PROMPT_COMMAND tìm ra nhánh git mà bạn có thể đang ở và hiển thị nhánh đó tại lời nhắc, cùng với trạng thái thoát của quá trình chạy cuối cùng, tên máy chủ và tên cơ sở của pwd. Biến RET lưu trữ giá trị trả về của chương trình được thực thi cuối cùng. Điều này thuận tiện để xem liệu có lỗi không và mã lỗi của chương trình cuối cùng tôi chạy trong thiết bị đầu cuối. Lưu ý bên ngoài 'bao quanh toàn bộ biểu thức PROMPT_COMMAND. Nó bao gồm PS1 để biến này được đánh giá lại mỗi khi biến PROMPT_COMMAND được đánh giá.

PROMPT_COMMAND='RET=$?;\
  BRANCH="";\
  ERRMSG="";\
  if [[ $RET != 0 ]]; then\
    ERRMSG=" $RET";\
  fi;\
  if git branch &>/dev/null; then\
    BRANCH=$(git branch 2>/dev/null | grep \* |  cut -d " " -f 2);\
  fi;
PS1="$GREEN\u@\h $BLUE\W $CYAN$BRANCH$RED$ERRMSG \$ $LIGHT_GRAY";'

Đầu ra ví dụ trông giống như thế này trong một thư mục không phải git:

sashan@dhcp-au-122 Documents  $ false
sashan@dhcp-au-122 Documents  1 $ 

và trong thư mục git, bạn thấy tên chi nhánh:

sashan@dhcp-au-122 rework mybranch $ 

Cập nhật

Sau khi đọc các bình luận và câu trả lời của Bob, tôi nghĩ rằng viết nó như anh ấy mô tả là tốt hơn. Nó dễ bảo trì hơn những gì tôi đã viết ban đầu ở trên, trong đó biến PS1 được đặt bên trong PROMPT_COMMAND, bản thân nó là một chuỗi siêu phức tạp được đánh giá trong thời gian chạy bằng bash. Nó hoạt động, nhưng nó phức tạp hơn mức cần thiết. Công bằng mà nói, tôi đã viết PROMPT_COMMAND đó cho chính mình khoảng 10 năm trước và nó đã hoạt động và không nghĩ quá nhiều về nó.

Đối với những người tò mò về cách tôi đã sửa đổi mọi thứ của mình, về cơ bản tôi đã đặt mã cho PROMPT_COMMAND trong tệp riêng biệt (như Bob đã mô tả) và sau đó lặp lại chuỗi mà tôi định là PS1:

GREEN="\[\033[0;32m\]"
CYAN="\[\033[0;36m\]"
RED="\[\033[0;31m\]"
PURPLE="\[\033[0;35m\]"
BROWN="\[\033[0;33m\]"
LIGHT_GRAY="\[\033[0;37m\]"
LIGHT_BLUE="\[\033[1;34m\]"
LIGHT_GREEN="\[\033[1;32m\]"
LIGHT_CYAN="\[\033[1;36m\]"
LIGHT_RED="\[\033[1;31m\]"
LIGHT_PURPLE="\[\033[1;35m\]"
YELLOW="\[\033[1;33m\]"
WHITE="\[\033[1;37m\]"
RESTORE="\[\033[0m\]" #0m restores to the terminal's default colour

if [ -z $SCHROOT_CHROOT_NAME ]; then
    SCHROOT_CHROOT_NAME=" "
fi
BRANCH=""
ERRMSG=""
RET=$1
if [[ $RET != 0 ]]; then
    ERRMSG=" $RET"
fi
if which git &>/dev/null; then
    BRANCH=$(git branch 2>/dev/null | grep \* |  cut -d " " -f 2)
else
    BRANCH="(git not installed)"
fi
echo "${GREEN}\u@\h${SCHROOT_CHROOT_NAME}${BLUE}\w \
${CYAN}${BRANCH}${RED}${ERRMSG} \$ $RESTORE"

và trong .bashrc của tôi

function prompt_command {
    RET=$?
    export PS1=$(~/.bash_prompt_command $RET)
}
PROMPT_DIRTRIM=3
export PROMPT_COMMAND=prompt_command

1
Bạn có thể rút ngắn một trong những dòng của bạn: if git branch &>/dev/null ; then\ . Nó chuyển hướng cả stdout và stderr đến / dev / null. tldp.org/LDP/abs/html/io-redirection.html

3
Không cần xuất PROMPT_COMMAND .
dolmen

2
Tôi nghĩ rằng bình luận của ceving cũng rất đúng cho câu trả lời này:Don't set PS1 in PROMPT_COMMAND! Set variables in PROMPT_COMMAND and use them in PS1
phil294

2
Tôi không thấy lý do, tại sao thay đổi PS1trực tuyến bên trong PROMPT_COMMANDlà bất lợi. Nó là mã hữu ích hoàn hảo. Ngược lại với câu trả lời của Bob, PS1biến đã được xây dựng chính xác. Điều này cho phép một lời nhắc bash phức tạp hơn nhiều tùy thuộc vào tình hình thực tế của bạn.
Christian Wolf

2
@ChristianWolf xây dựng PS1bên trong PROMPT_COMMANDkhông có mục đích gì. nó là một ví dụ về cách không làm điều đó. tạo PS1một lần trong .bash_profile, chỉ sử dụng dấu nháy đơn thay vì dấu nháy kép, do đó các thay thế biến sẽ được đánh giá trong mỗi lần nhắc.
bạn bè

46

Sự khác biệt là PS1 là chuỗi lời nhắc thực sự được sử dụng và PROMPT_COMMAND là lệnh được thực thi ngay trước dấu nhắc. Nếu bạn muốn cách đơn giản nhất, linh hoạt nhất để tạo lời nhắc, hãy thử cách này:

Đặt cái này vào .bashrc của bạn:

function prompt_command {
  export PS1=$(~/bin/bash_prompt)
}
export PROMPT_COMMAND=prompt_command

Sau đó, viết một script (bash, perl, ruby: sự lựa chọn của bạn) và đặt nó vào ~ / bin / bash_prompt.

Tập lệnh có thể sử dụng bất kỳ thông tin nào nó thích để tạo lời nhắc. IMO này đơn giản hơn nhiều vì bạn không phải học ngôn ngữ thay thế hơi baroque vốn được phát triển chỉ cho biến PS1.

Bạn có thể nghĩ rằng bạn có thể làm điều tương tự bằng cách đơn giản đặt PROMPT_COMMAND trực tiếp thành ~ / bin / bash_prompt và đặt PS1 thành chuỗi trống. Điều này thoạt đầu có vẻ hoạt động, nhưng bạn sớm phát hiện ra rằng mã dòng đọc mong đợi PS1 được đặt thành lời nhắc thực tế và khi bạn cuộn từ khóa ngược trong lịch sử, kết quả là mọi thứ sẽ rối tung lên. Cách giải quyết này khiến PS1 luôn phản ánh lời nhắc mới nhất (vì hàm đặt PS1 thực tế được sử dụng bởi phiên bản gọi của trình bao) và điều này làm cho lịch sử đọc và lệnh hoạt động tốt.


17
Đừng PS1tham gia PROMPT_COMMAND! Đặt các biến trong PROMPT_COMMANDvà sử dụng chúng trong PS1. Nếu không, bạn sẽ mất khả năng sử dụng các PS1chuỗi thoát như \uhoặc \h. Bạn phải phát minh lại chúng PROMPT_COMMAND. Điều đó có thể khả thi nhưng không thể giải quyết việc mất dấu \[\]đánh dấu phần đầu và phần cuối của các ký tự không in được. Điều này có nghĩa là bạn không thể sử dụng màu sắc mà không gây nhầm lẫn cho thiết bị đầu cuối về độ dài của lời nhắc. Và điều này gây nhầm lẫn readlinekhi chỉnh sửa một lệnh sinh ra hai dòng. Cuối cùng bạn có một mớ hỗn độn lớn trên màn hình.
thúc

1
@ceving Đúng vậy! Người ta có thể sử dụng PROMPT_COMMAND để thay đổi định dạng PS1 của bạn và tận dụng tối đa cả hai thế giới
2grit,

3
PROMPT_COMMANDđược thực thi trước khi in PS1. Tôi thấy không có vấn đề gì khi thiết lập PS1từ bên trong PROMPT_COMMAND, vì sau khi PROMPT_COMMANDhoàn tất, trình bao sẽ in PS1, được sửa đổi từ PROMPT_COMMAND(hoặc trong trường hợp này là bên trong prompt_command)?
Felipe Alvarez,

3
Cảnh báo: Nói chung không nên sử dụng PROMPT_COMMAND để in các ký tự trực tiếp vào lời nhắc. Các ký tự được in bên ngoài PS1 không được tính bằng Bash, điều này sẽ khiến nó đặt con trỏ và ký tự xóa không chính xác. Sử dụng PROMPT_COMMAND để đặt PS1 hoặc xem các lệnh nhúng. ( Nguồn Arch Wiki )
meffect

3
tôi không nhận được nó tại sao tất cả mọi người cố gắng để làm một số thủ thuật trong PROMPT_COMMAND thay vì chỉ sử dụng thay thế lệnh trong PS1 export PS1='$(~/bin/bash_prompt)'không cùng một điều lỗi vẻ lành mạnh
bạn thân

10

Từ man bash:

PROMPT_COMMAND

Nếu được đặt, giá trị sẽ được thực thi dưới dạng lệnh trước khi phát hành mỗi dấu nhắc chính.

PS1

Giá trị của tham số này được mở rộng (xem PROMPTING bên dưới) và được sử dụng làm chuỗi dấu nhắc chính. Giá trị mặc định là '' \ s- \ v \ $ ''.

Nếu bạn chỉ muốn đặt chuỗi lời nhắc, chỉ sử dụng PS1một mình là đủ:

PS1='user \u on host \h$ '

Nếu bạn muốn làm việc khác ngay trước khi in lời nhắc, hãy sử dụng PROMPT_COMMAND. Ví dụ: nếu bạn muốn đồng bộ hóa các ghi trong bộ nhớ cache vào đĩa, bạn có thể viết:

PROMPT_COMMAND='sync'

1
Bạn cũng có thể thiết lập các tiêu đề của thiết bị đầu cuối từ PS1mà không cần PROMPT_COMMAND, như trình tự mà đặt tiêu đề có thể được bao gồm trong PS1được bao bọc bởi \[\].
dolmen

1
@dolmen Được rồi. Sau đó, hãy làm điều gì đó khác, chẳng hạn như thiết lập động một biến môi trường.
Cyker

@Cyker bạn có thể đặt động biến môi trường trong PS1đó, nó sẽ chỉ được đặt trong vỏ con, vì vậy bạn không thể lấy lại giá trị của nó. nhưng ví dụ của bạn là tầm thườngPS1='$(sync)user \u on host \h$ '
bạn bè

1

sự khác biệt là

  • nếu bạn xuất dòng không hoàn chỉnh từ PROMPT_COMMAND, nó sẽ làm hỏng lời nhắc bash của bạn
  • PS1người thay thế \Hvà bạn bè
  • PROMPT_COMMANDchạy nội dung của nó, PS1sử dụng nội dung của nó như lời nhắc.

PS1mở rộng biến và thay thế lệnh tại mỗi dấu nhắc, không cần sử dụng PROMPT_COMMANDđể gán giá trị cho PS1hoặc để chạy mã tùy ý. bạn có thể dễ dàng thực hiện export PS1='$(uuidgen) $RANDOM'một lần .bash_profile, chỉ cần sử dụng dấu ngoặc kép


0

Vâng, vì vậy để cố gắng thực sự khắc phục điều này:

  • PROMPT_COMMANDlà một biến / hàm tiện lợi bash tiện dụng , nhưng nói đúng ra thì không có gì cũng không thể thực hiện được khi sử dụng PS1một mình, đúng không?

Ý tôi là, nếu một người muốn đặt một biến khác có phạm vi bên ngoài dấu nhắc: tùy thuộc vào trình bao, biến đó có thể sẽ cần được khai báo bên ngoài trước $PS1hoặc (trường hợp xấu nhất) người ta có thể phải thích thú với một thứ gì đó đang chờ trên FIFO trước khi gọi $PS1(và trang bị lại ở cuối $PS1); nó \u \hcó thể gây ra một số rắc rối, đặc biệt nếu bạn đang sử dụng một số regex ưa thích; nhưng nếu không: người ta có thể hoàn thành bất cứ điều gì PROMPT_COMMANDcó thể bằng cách sử dụng thay thế lệnh bên trong $PS1(và, có thể trong các trường hợp góc, các bảng con rõ ràng)?

Đúng?

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.