Căn lề phải của dấu nhắc


27

Tôi chắc chắn rằng tôi đã thấy ai đó có một phần dấu nhắc của họ được căn phải ở bên phải trong cửa sổ đầu cuối của họ và sau đó bắt đầu con trỏ thực sự trên một dòng thứ hai. Tôi biết rằng tôi có thể đạt được dòng thứ hai với "\ n" trong PS1, nhưng tôi không thể tìm ra cách sắp xếp một phần của nó sang bên phải. Là những gì tôi thấy chỉ là khoảng trắng được thêm vào giữa hai chuỗi?

Câu trả lời:


17

Những gì bạn muốn có thể khá dễ dàng được thực hiện bằng cách hiển thị dòng đầu tiên trước khi hiển thị lời nhắc. Ví dụ, phần sau đây hiển thị lời nhắc \wbên trái của dòng đầu tiên và lời nhắc \u@\hbên phải của dòng đầu tiên. Nó sử dụng $COLUMNSbiến chứa chiều rộng của thiết bị đầu cuối và $PROMPT_COMMANDtham số được ước tính trước khi bash hiển thị lời nhắc.

print_pre_prompt () 
{ 
    PS1L=$PWD
    if [[ $PS1L/ = "$HOME"/* ]]; then PS1L=\~${PS1L#$HOME}; fi
    PS1R=$USER@$HOSTNAME
    printf "%s%$(($COLUMNS-${#PS1L}))s" "$PS1L" "$PS1R"
}
PROMPT_COMMAND=print_pre_prompt

3
Lưu ý rằng mọi thứ trở nên phức tạp hơn đáng kể nếu bạn muốn một dấu nhắc bên trái có màu, vì các ký tự không in có nghĩa là độ dài chuỗi không giống với số lượng ký tự được hiển thị.
Mu Mind

1
Cả câu trả lời này và câu trả lời được bình chọn cao nhất đều không hoạt động chính xác nếu .inputrcset show-mode-in-prompt on. Cả hai đều không tính toán độ dài của mã ANSI CSI không thể định dạng được và không bao gồm chúng một cách chính xác \[\]như được đề cập bởi @Mu Mind. Xem câu trả lời này cho một giải pháp.
Tom Hale

26

Dựa trên thông tin tôi tìm thấy ở đây, tôi có thể khám phá một giải pháp đơn giản hơn để căn phải trong khi điều chỉnh nội dung có độ dài thay đổi ở bên phải hoặc bên trái, bao gồm hỗ trợ cho màu sắc. Đã thêm vào đây để thuận tiện cho bạn ...

Lưu ý về màu sắc: sử dụng \033lối thoát có lợi cho các lựa chọn thay thế, không có \[\]nhóm, chứng tỏ khả năng tương thích nhất và do đó được khuyến nghị.

Mẹo nhỏ là viết phía bên tay phải trước, sau đó sử dụng quay trở lại ( \r) để trở về đầu dòng và tiếp tục ghi đè lên nội dung phía bên trái trên đó, như sau:

prompt() {
    PS1=$(printf "%*s\r%s\n\$ " "$(tput cols)" 'right' 'left')
}
PROMPT_COMMAND=prompt

Tôi đang sử dụng tput colstrên Mac OS X để lấy chiều rộng của thiết bị đầu cuối / bàn điều khiển terminfo$COLUMNSvar của tôi không được nhập vào envnhưng bạn có thể thay thế *giá trị "" có thể thay thế %*sbằng cách cung cấp " ${COLUMNS}" hoặc bất kỳ giá trị nào khác bạn muốn, thay vào đó.

Ví dụ tiếp theo sử dụng $RANDOMđể tạo nội dung có độ dài khác nhau bao gồm màu sắc và hiển thị cách bạn có thể trích xuất các hàm để cấu trúc lại việc triển khai thành các hàm có thể sử dụng lại.

function prompt_right() {
  echo -e "\033[0;36m$(echo ${RANDOM})\033[0m"
}

function prompt_left() {
  echo -e "\033[0;35m${RANDOM}\033[0m"
}

function prompt() {
    compensate=11
    PS1=$(printf "%*s\r%s\n\$ " "$(($(tput cols)+${compensate}))" "$(prompt_right)" "$(prompt_left)")
}
PROMPT_COMMAND=prompt

printfgiả sử độ dài của chuỗi là # ký tự mà chúng ta cần để bù cho số lượng ký tự được yêu cầu để hiển thị màu sắc, bạn sẽ thấy nó luôn luôn ở cuối màn hình vì các ký tự ANSI không được in mà không được bù. Các ký tự được yêu cầu cho màu vẫn không đổi và bạn sẽ thấy rằng printf cũng tính đến sự thay đổi về độ dài, như được trả về $RANDOMchẳng hạn ', giúp giữ cho sự liên kết đúng của chúng ta trong chiến thuật.

Đây không phải là trường hợp với trình tự nhắc thoát bash đặc biệt (ví dụ. \u, \w, \h, \t) Tuy nhiên, vì chúng sẽ chỉ ghi lại một chiều dài của 2 vì bash sẽ chỉ dịch chúng khi nhắc được hiển thị, sau printf đã trả lại chuỗi. Điều này không ảnh hưởng đến phía bên tay trái nhưng tốt nhất là tránh chúng ở bên phải.

Không có hậu quả nếu nội dung được tạo sẽ vẫn ở độ dài không đổi. Giống như với \ttùy chọn thời gian sẽ luôn hiển thị cùng số lượng ký tự (8) trong 24 lần. Chúng ta chỉ cần tính đến mức bù cần thiết để phù hợp với sự khác biệt giữa 2 ký tự được tính mà kết quả là 8 ký tự khi được in, trong những trường hợp này.

Hãy nhớ rằng bạn có thể cần phải thoát ba lần thoát \\\một số chuỗi thoát mà nếu không thì có ý nghĩa với chuỗi. Như với ví dụ sau, thoát thư mục làm việc hiện tại \wkhông có ý nghĩa gì khác vì vậy nó hoạt động như mong đợi nhưng thời gian \t, có nghĩa là một ký tự tab, không hoạt động như mong đợi mà không thoát ba lần đầu tiên.

function prompt_right() {
  echo -e "\033[0;36m\\\t\033[0m"
}

function prompt_left() {
  echo -e "\033[0;35m\w\033[0m"
}

function prompt() {
    compensate=5
    PS1=$(printf "%*s\r%s\n\$ " "$(($(tput cols)+${compensate}))" "$(prompt_right)" "$(prompt_left)")
}
PROMPT_COMMAND=prompt

Xin chào!


8

Sử dụng printfvới $COLUMNSlàm việc thực sự tốt, một cái gì đó như:

printf "%${COLUMNS}s\n" "hello"

Nó đúng biện minh nó hoàn hảo cho tôi.


6

Sau đây sẽ đưa ngày và giờ hiện tại vào ĐỎ trên RHS của thiết bị đầu cuối.

# Create a string like:  "[ Apr 25 16:06 ]" with time in RED.
printf -v PS1RHS "\e[0m[ \e[0;1;31m%(%b %d %H:%M)T \e[0m]" -1 # -1 is current time

# Strip ANSI commands before counting length
# From: https://www.commandlinefu.com/commands/view/12043/remove-color-special-escape-ansi-codes-from-text-with-sed
PS1RHS_stripped=$(sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" <<<"$PS1RHS")

# Reference: https://en.wikipedia.org/wiki/ANSI_escape_code
local Save='\e[s' # Save cursor position
local Rest='\e[u' # Restore cursor to save point

# Save cursor position, jump to right hand edge, then go left N columns where
# N is the length of the printable RHS string. Print the RHS string, then
# return to the saved position and print the LHS prompt.

# Note: "\[" and "\]" are used so that bash can calculate the number of
# printed characters so that the prompt doesn't do strange things when
# editing the entered text.

PS1="\[${Save}\e[${COLUMNS:-$(tput cols)}C\e[${#PS1RHS_stripped}D${PS1RHS}${Rest}\]${PS1}"

Ưu điểm:

  • Hoạt động chính xác với màu sắc và mã ANSI CSI trong lời nhắc RHS
  • Không có quy trình con. shellcheckdọn dẹp.
  • Hoạt động chính xác nếu .inputrcset show-mode-in-prompt on.
  • Đóng gói chính xác các ký tự không có độ dài không nhắc trong \[\]do đó, việc chỉnh sửa văn bản được nhập tại dấu nhắc không gây ra dấu nhắc in lại một cách kỳ lạ.

Lưu ý : Bạn sẽ cần đảm bảo rằng bất kỳ chuỗi màu nào trong $PS1trước khi mã này được thực thi đều được đính kèm đúng cách \[\]không có lồng nhau của chúng.


Mặc dù tôi thực sự thích cách tiếp cận này về mặt lý thuyết, nhưng trên thực tế, nó không hoạt động (ubfox 18.04, GNU bash 4.4.19): nối mã trực tiếp vào .bashrc trước tiên đưa ra lỗi bash: local: can only be used in a function, đây là lỗi không đáng để sửa, và sau đó, nó không hiển thị bất cứ điều gì vì COLUMNSkhông được xác định: nó phải được thay thế bằng $(tput cols). kết quả tương tự nếu đoạn mã được lưu trên một tệp khác và sau đó có nguồn gốc .bashrc.
Polentino

1
Cảm ơn @Polentino. Tôi đã cập nhật mã để chạy tput colsnếu $COLUMNSkhông được đặt. Và vâng, mã này phải nằm trong một hàm. Tôi sử dụng PROMPT_COMMAND='_prompt_bash_set'và đặt tên cho chức năng _prompt_bash_set.
Tom Hale

2

Tôi chỉ nghĩ rằng tôi sẽ ném của tôi vào đây. Nó gần giống hệt như lời nhắc zsh GRML (ngoại trừ các bản cập nhật zsh, nó nhắc tốt hơn một chút về các dòng mới và khoảng trắng phía sau - ít nhất là không thể sao chép trong bash ... ít nhất là rất khó vào thời điểm này).

Tôi đã dành ba ngày tốt cho việc này (chỉ được thử nghiệm trên máy tính xách tay chạy vòm), vì vậy đây là ảnh chụp màn hình và sau đó là nội dung trong ~ / .bashrc của tôi :)

ảnh chụp màn hình của dấu nhắc bash trong hành động

cảnh báo - đó là một chút điên rồ

quan trọng sang một bên - mỗi ^[(như ^[[34m) thực sự là nhân vật thoát (char)27. Cách duy nhất tôi biết cách chèn cái này là nhập ctrl+ ( [v) (tức là nhấn cả hai [vtrong khi nhấn và ctrlgiữ.

# grml battery?
GRML_DISPLAY_BATTERY=1

# battery dir
if [ -d /sys/class/power_supply/BAT0 ]; then
    _PS1_bat_dir='BAT0';
else
    _PS1_bat_dir='BAT1';
fi

# ps1 return and battery
_PS1_ret(){
    # should be at beg of line (otherwise more complex stuff needed)
    RET=$?;

    # battery
    if [[ "$GRML_DISPLAY_BATTERY" == "1" ]]; then
        if [ -d /sys/class/power_supply/$_PS1_bat_dir ]; then
            # linux
            STATUS="$( cat /sys/class/power_supply/$_PS1_bat_dir/status )";
            if [ "$STATUS" = "Discharging" ]; then
                bat=$( printf ' v%d%%' "$( cat /sys/class/power_supply/$_PS1_bat_dir/capacity )" );
            elif [ "$STATUS" = "Charging" ]; then
                bat=$( printf ' ^%d%%' "$( cat /sys/class/power_supply/$_PS1_bat_dir/capacity )" );
            elif [ "$STATUS" = "Full" ] || [ "$STATUS" = "Unknown" ] && [ "$(cat /sys/class/power_supply/$_PS1_bat_dir/capacity)" -gt "98" ]; then
                bat=$( printf ' =%d%%' "$( cat /sys/class/power_supply/$_PS1_bat_dir/capacity )" );
            else
                bat=$( printf ' ?%d%%' "$( cat /sys/class/power_supply/$_PS1_bat_dir/capacity )" );
            fi;
        fi
    fi

    if [[ "$RET" -ne "0" ]]; then
        printf '\001%*s%s\r%s\002%s ' "$(tput cols)" ":( $bat " "^[[0;31;1m" "$RET"
    else
        printf '\001%*s%s\r\002' "$(tput cols)" "$bat "
    fi;
}

_HAS_GIT=$( type 'git' &> /dev/null );

# ps1 git branch
_PS1_git(){
    if ! $_HAS_GIT; then
        return 1;
    fi;
    if [ ! "$( git rev-parse --is-inside-git-dir 2> /dev/null )" ]; then
        return 2;
    fi
    branch="$( git symbolic-ref --short -q HEAD 2> /dev/null )"

    if [ "$branch" ]; then
        printf ' \001%s\002(\001%s\002git\001%s\002)\001%s\002-\001%s\002[\001%s\002%s\001%s\002]\001%s\002' "^[[0;35m" "^[[39m" "^[[35m" "^[[39m" "^[[35m" "^[[32m" "${branch}" "^[[35m" "^[[39m"
    fi;
}

# grml PS1 string
PS1="\n\[\e[F\e[0m\]\$(_PS1_ret)\[\e[34;1m\]${debian_chroot:+($debian_chroot)}\u\[\e[0m\]@\h \[\e[01m\]\w\$(_PS1_git) \[\e[0m\]% "

Tôi vẫn đang làm việc để làm cho màu sắc có thể cấu hình được, nhưng tôi hài lòng với màu sắc như hiện tại.


Hiện đang làm việc với một bản sửa lỗi cho ^[nhân vật điên và chuyển đổi màu dễ dàng :)


Đó không phải là Ctrl + [và v đồng thời, đó là Ctrl + v theo sau là Ctrl + [.
NieDzejkob


0

Thêm vào câu trả lời của Giles, tôi đã viết một cái gì đó để xử lý màu sắc tốt hơn (miễn là chúng được bao bọc đúng cách \[\]tùy từng trường hợp và không xử lý mọi trường hợp, nhưng nó cho phép tôi đặt PS1L theo cú pháp giống như PS1 và sử dụng ngày (không màu) là PS1R.

function title {
    case "$TERM" in
    xterm*|rxvt*)
        echo -en "\033]2;$1\007"
        ;;
    *)
        ;;
    esac
}

print_pre_prompt() {
    PS1R=$(date)
    PS1L_exp="${PS1L//\\u/$USER}"
    PS1L_exp="${PS1L_exp//\\h/$HOSTNAME}"
    SHORT_PWD=${PWD/$HOME/~}
    PS1L_exp="${PS1L_exp//\\w/$SHORT_PWD}"
    PS1L_clean="$(sed -r 's:\\\[([^\\]|\\[^]])*\\\]::g' <<<$PS1L_exp)"
    PS1L_exp=${PS1L_exp//\\\[/}
    PS1L_exp=${PS1L_exp//\\\]/}
    PS1L_exp=$(eval echo '"'$PS1L_exp'"')
    PS1L_clean=$(eval echo -e $PS1L_clean)
    title $PS1L_clean
    printf "%b%$(($COLUMNS-${#PS1L_clean}))b\n" "$PS1L_exp" "$PS1R"
}

Đây là trên github: dbarnett / dotfiles / right_prompt.sh . Tôi sử dụng nó trong .bashrc của tôi như thế này:

source $HOME/dotfiles/right_prompt.sh
PS1L='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]'
PS1='\[\033[01;34m\]\w\[\033[00m\]\$ '
PROMPT_COMMAND=print_pre_prompt

Lưu ý: Tôi cũng đã thêm một dòng mới sau PS1R, điều này không có sự khác biệt về hình ảnh, nhưng dường như giữ cho lời nhắc không bị cắt xén nếu bạn cuộn qua các lệnh nhất định trong lịch sử lệnh của mình.

Tôi chắc chắn rằng ai đó khác có thể cải thiện vấn đề này và có thể khái quát một số trường hợp đặc biệt.


0

Đây là một giải pháp dựa trên PROMPT_COMMANDtput:

function __prompt_command() {
  local EXIT="$?"             # This needs to be first
  history -a
  local COL=$(expr `tput cols` - 8)
    PS1="💻 \[$(tput setaf 196)\][\[$(tput setaf 21)\]\W\[$(tput setaf 196)\]]\[$(tput setaf 190)\]"
    local DATE=$(date "+%H:%M:%S")
  if [ $EXIT != 0 ]; then
    PS1+="\[$(tput setaf 196)\]\$"      # Add red if exit code non 0
    tput sc;tput cuu1; tput cuf $COL;echo "$(tput setaf 196)$DATE"; tput rc
  else
  PS1+="\[$(tput setaf 118)\]\$"
    tput sc;tput cuu1; tput cuf $COL;echo "$(tput setaf 118)$DATE"; tput rc
  fi
  PS1+="\[$(tput setaf 255)\] "
}
PROMPT_COMMAND="__prompt_command"

Phép thuật được thực hiện bởi:

tput sc;tput cuu1; tput cuf $COL;echo "$(tput setaf 196)$DATE"; tput rc

Được chia nhỏ theo:

tput sc                       # saved the cursor position
tput cuu1                     # up one line
tput cuf $COL                 # move $COL characters left
echo "$(tput setaf 196)$DATE" # set the colour and print the date
tput rc                       # restore the cursor position

Trong PS1, tputđược thoát với \ [\] để nó không được tính vào thời lượng hiển thị.

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.