Dấu nhắc bash nhỏ gọn khi sử dụng cây thư mục / tên tệp


16

Trong một hệ thống với Ubuntu 14.04 và bash, tôi có PS1biến kết thúc với các nội dung sau:

\u@\h:\w\$

để lời nhắc xuất hiện dưới dạng

user@machinename:/home/mydirectory$

Tuy nhiên, đôi khi, thư mục hiện tại có một tên dài hoặc nó nằm trong các thư mục có tên dài, để lời nhắc đó trông giống như

user@machinename:/home/mydirectory1/second_directory_with_a_too_long_name/my_actual_directory_with_another_long_name$

Điều này sẽ điền vào dòng trong thiết bị đầu cuối và con trỏ sẽ đi đến một dòng khác, gây khó chịu.

Thay vào đó tôi muốn có được một cái gì đó như

user@machinename:/home/mydirectory1/...another_long_name$

Có cách nào để xác định PS1biến để "bọc" và "thu gọn" tên thư mục, để không bao giờ vượt quá một số ký tự nhất định, có được một dấu nhắc ngắn hơn không?


1
May mắn thay, tôi nhớ nơi tôi đã đọc cách tùy chỉnh dấu nhắc shell: tldp.org/HOWTO/Bash-Prompt-HOWTO/x783.html Cảm ơn Giles Orr, tác giả của Bash Prompt HOWTO và những người đã đóng góp cho nó.
thân

Xem thêm unix.stackexchange.com/a/216871/117549 (dựa trên ksh, nhưng ý tưởng tương tự)
Jeff Schaller

Câu trả lời:


16

Trước hết, bạn có thể chỉ muốn thay đổi \wvới \W. Theo cách đó, chỉ có tên của thư mục hiện tại được in và không phải toàn bộ đường dẫn của nó:

terdon@oregano:/home/mydirectory1/second_directory_with_a_too_long_name/my_actual_directory_with_another_long_name $ PS1="\u@\h:\W \$ "
terdon@oregano:my_actual_directory_with_another_long_name $ 

Điều đó có thể vẫn chưa đủ nếu tên thư mục quá dài. Trong trường hợp đó, bạn có thể sử dụng PROMPT_COMMANDbiến cho việc này. Đây là một biến bash đặc biệt có giá trị được thực thi dưới dạng lệnh trước khi mỗi dấu nhắc được hiển thị. Vì vậy, nếu bạn đặt chức năng đó thành một hàm đặt lời nhắc mong muốn dựa trên độ dài đường dẫn của thư mục hiện tại của bạn, bạn có thể nhận được hiệu ứng mà bạn theo sau. Ví dụ: thêm các dòng này vào ~/.bashrc:

get_PS1(){
        limit=${1:-20}
        if [[ "${#PWD}" -gt "$limit" ]]; then
                ## Take the first 5 characters of the path
                left="${PWD:0:5}"
                ## ${#PWD} is the length of $PWD. Get the last $limit
                ##  characters of $PWD.
                right="${PWD:$((${#PWD}-$limit)):${#PWD}}"
                PS1="\[\033[01;33m\]\u@\h\[\033[01;34m\] ${left}...${right} \$\[\033[00m\] "
        else
                PS1="\[\033[01;33m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] "
        fi


}
PROMPT_COMMAND=get_PS1

Hiệu ứng trông như thế này:

terdon@oregano ~ $ cd /home/mydirectory1/second_directory_with_a_too_long_name/my_actual_directory_with_another_long_name
terdon@oregano /home...th_another_long_name $ 

10

Thêm vào một ký tự trở lại là giải pháp chính của tôi cho điều này

Vì vậy, lời nhắc của tôi (cũng có những thứ khác nữa, làm cho nó dài hơn nữa) trông như thế này:

nhập mô tả hình ảnh ở đây

Bạn sẽ nhận thấy rằng $ đang được trả lại dưới dạng một dòng mới

Tôi đạt được điều này với

HOST='\[\033[02;36m\]\h'; HOST=' '$HOST
TIME='\[\033[01;31m\]\t \[\033[01;32m\]'
LOCATION=' \[\033[01;34m\]`pwd | sed "s#\(/[^/]\{1,\}/[^/]\{1,\}/[^/]\{1,\}/\).*\(/[^/]\{1,\}/[^/]\{1,\}\)/\{0,1\}#\1_\2#g"`'
PS1=$TIME$USER$HOST$LOCATION'\n\$ '

Lưu ý rằng, mặc dù trên một dòng riêng biệt, các cây thư mục siêu dài như

/home/durrantm/Dropbox/96_2013_archive/work/code/ruby__rails được rút ngắn thành

/home/durrantm/Dropbox/_/code/ruby__rails

tức là "top 3 thư mục / _ / dưới hai thư mục" thường là điều tôi quan tâm

Điều này sẽ đảm bảo rằng dòng không bao giờ quá dài do chiều dài cây thư mục. Nếu bạn luôn muốn cây thư mục đầy đủ, chỉ cần điều chỉnh VỊ TRÍ, tức là

LOCATION=' \[\033[01;34m\]`pwd`'

Thêm thông tin về màu sắc tại unix.stackexchange.com/q/124407/10043
Michael Durrant

1
Trường hợp của bạn thực sự bao gồm $ ? (Xem cái này .)
G-Man nói 'Phục hồi Monica'

Đã thêm \ $ vào cuối, cảm ơn. đó là bởi vì thông thường tôi cũng cho thấy chi nhánh git của mình nhưng đó là quá nhiều chi tiết ở đây
Michael Durrant

1
Gì? Không có dòng mới? Không có SGR0?
G-Man nói 'Phục hồi Monica'

Đã thêm linefeed
Michael Durrant

3

Đã tạo ~ / .bash_prompt:

maxlen=36
# set leftlen to zero for printing just the right part of the path
leftlen=19
shortened="..."
# Default PWD
nPWD=${PWD}
if [ ${#nPWD} -gt $maxlen ]; then
  offset=$(( ${#nPWD} - $maxlen + $leftlen ))
  nPWD="${nPWD:0:$leftlen}${shortened}${nPWD:$offset:$maxlen}"
else
  nPWD='\w'
fi
echo "\u@\h:$nPWD\$ "

Đã thêm vào ~ / .bash_profile của tôi:

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

Đầu ra là:

user@machinename:/home/mydirectory1/...another_long_name$

1

Không phải là một giải pháp để rút ngắn các đường dẫn dài, nhưng một cách thuận tiện để có được cái nhìn tổng quan tốt hơn trong khi giữ cho tất cả thông tin đường dẫn hiển thị, là thêm một dòng mới trước ký tự cuối cùng. Bằng cách này, con trỏ bắt đầu luôn trong cùng một cột, ngay cả khi đường dẫn đủ dài để bao quanh, nhưng (các) cửa sổ bảng điều khiển của bạn phải đủ cao để không cuộn ra các dòng trước quá nhanh. Tôi đã xóa các mã màu cho rõ ràng hơn:

murphy@seasonsend:~
$ echo $PS1
\u@\h:\w\n\$
murphy@seasonsend:~
$ 

1

Tôi sử dụng cái này, nó bao bọc trên nhiều dòng và thụt theo độ dài user@hostđể nó giả sử dòng điện PS1có hiệu quả ' \u@\h:\w$'. Nó không cắt bớt đường dẫn và nó thích nghi với chiều rộng của thiết bị đầu cuối hiện tại. Nó chỉ phân chia đường dẫn trên /, vì vậy nó không xử lý một cách tao nhã với các thư mục thực sự dài (nhưng nó giữ được không gian cho lựa chọn / sao chép). Nó đảm bảo bạn luôn có sẵn ít nhất 20 ký tự cho đầu vào.

readonly _PS1="${PS1}" 2>/dev/null

function myprompt()
{
    local IFS
    local nn nb pbits xpwd="" ww=60 len=0 pp='\\w\$ '
    local indent uh="${LOGNAME}@${HOSTNAME//.*/}"

    test -n "$COLUMNS" && let ww=$COLUMNS-20  # may be unset at startup

    PS1="${_PS1}"
    if [ ${#PWD} -ge $ww ]; then
        printf -v indent "%${#uh}s%s" " " "> "  # indent strlen(user@host)

        IFS=/ pbits=( $PWD ); unset IFS
        nb=${#pbits[*]}
        for ((nn=1; nn<nb; nn++)) {
            if [ $(( $len + 1 + ${#pbits[$nn]} )) -gt $ww ]; then
                xpwd="${xpwd}/...\n${indent}..."
                len=0
            fi
            xpwd="${xpwd}/${pbits[$nn]}"
            let len=len+1+${#pbits[$nn]}
        }
        # add another newline+indent if the input space is too tight
        if (( ( ${#uh} + len ) > ww )); then
            printf -v xpwd "${xpwd}\n%${#uh}s" " " 
        fi 
        PS1="${PS1/$pp/$xpwd}$ "    
    fi
}
PROMPT_COMMAND=myprompt

Điều này hoạt động bằng cách lấy ma thuật \w(chỉ khớp \w$với cái này) ra PS1và thay thế nó $PWD, sau đó gói nó thành một chuỗi ký tự đơn giản. Nó tính toán lại PS1mỗi lần từ giá trị ban đầu được lưu vào _PS1, điều này có nghĩa là các lối thoát "vô hình" cũng được giữ nguyên, chuỗi dấu nhắc ban đầu đầy đủ của tôi cho xtermvà dấu nhắc đậm:

PS1="\[\033]0;\u@\h:\w\007\]\[$(tput bold)\]\u@\h\[$(tput sgr0)\]:\w$ "

Và kết quả cuối cùng trong một thiết bị đầu cuối 80 cột:

mr@onomatopoeia:~$ cd /usr/src/linux/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace
mr@onomatopoeia:/usr/src/linux/tools/perf/scripts/perl/Perf-Trace-Util/lib/...
               > .../Perf/Trace$ _

Điều này hoạt động từ bash-3.2 như printf -v varđược sử dụng. Do sự phức tạp khác nhau, nó sẽ cần một số điều chỉnh cho các biến thể khác của PS1.

(Đường dẫn trong thanh tiêu đề xterm không được bọc hay viết tắt, điều gì đó có thể được thực hiện bằng cách kết hợp một trong những câu trả lời khác ở đây vào hàm trên.)


0

Thay vào đó, trong .zshrc của tôi, tôi viết tắt thành chữ cái đầu tiên của mỗi thư mục nếu chiều rộng pixel vượt quá chiều rộng nhất định:

user@machinename:/home/mydirectory1/second_directory
user@machinename:/home/mydirectory1/second_directory/my_actual_directory

trở thành:

user@machinename:/h/mydirectory1/second_directory
user@machinename:/h/m/s/my_actual_directory

Đây là hàm zsh để làm như vậy:

     # get the path
     t=`print -P "%m:%~"`;
     t=`echo $t | sed -r 's/([^:])[^:]*([0-9][0-9]):|([^:])[^:]*([^:]):/\1\2\3\4:/'`;
     oldlen=-1;

     # create 4 buckets of letters by their widths
     t1="${t//[^ijlIFT]}";
     t2="${t//[ijlIFTGoQMmWABEKPSVXYCDHNRUw]}";
     t3="${t//[^ABEKPSVXYCDHNRUw]}";
     t4="${t//[^GoQMmW]}";

     # keep abbreviating parent directories in the path until under 456 pixels
     while (( ( ( ${#t1} * 150 ) + ( ${#t2} * 178 ) + ( ${#t3} * 190 ) + ( ${#t4} * 201 ) ) > 4560 && ${#t}!=oldlen)) {
       oldlen=${#t};
       t=`echo $t | sed 's/\/\(.\)[^\/][^\/]*\//\/\1\//'`;
       t1="${t//[^ijlIFT]}";
       t2="${t//[ijlIFTGoQMmWABEKPSVXYCDHNRUw]}";
       t3="${t//[^ABEKPSVXYCDHNRUw]}";
       t4="${t//[^GoQMmW]}";
     };

     PS1=$t

Tôi thực sự sử dụng điều này để cập nhật tiêu đề thiết bị đầu cuối để với nhiều tab, tôi có thể giữ thẳng tab nào. Toàn bộ .zshrc để làm điều này là ở đây .

Điều này rất tiện dụng vì nó giữ ngữ cảnh và trong zsh cho phép bạn nhanh chóng tab hoàn thành một thư mục có cùng định dạng. (ví dụ: gõ cd /h/m/s/<tab>sẽ tự động hoàn thành cd /home/mydirectory1/second_directory)


Làm thế nào điều này áp dụng cho câu hỏi mà OP đặt ra, về cách xác định dấu nhắc PS1?
Anthon

Được chỉnh sửa cho rõ ràng, $ t trở thành PS1
Richard

0

Hãy thử sử dụng tập lệnh Python này . Nó cắt các phần riêng lẻ của tên đường dẫn, chính xác như cách bạn muốn trong câu hỏi của mình. Nó cũng sử dụng dấu chấm lửng unicode chỉ chiếm một cột thay vì ba.

Ví dụ đầu ra cho đường dẫn của bạn (khi được giới hạn 30 ký tự):

/home/mydir…/second…/my_actua

Điều đáng chú ý là giải pháp này xử lý chính xác Unicode trong tên thư mục bằng cách sử dụng wcswidth. ${#PWD}, mà các câu trả lời khác đã sử dụng, sẽ đánh giá sai chiều rộng hình ảnh của bất kỳ đường dẫn nào chứa các ký tự UTF-8.

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.