Компактная подсказка bash при использовании дерева каталогов / имени файла


16

В системе с Ubuntu 14.04 и у bashменя есть PS1переменная, заканчивающаяся следующим содержанием:

\u@\h:\w\$

так что подсказка выглядит как

user@machinename:/home/mydirectory$

Однако иногда текущий каталог имеет длинное имя или находится внутри каталогов с длинными именами, так что приглашение выглядит

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

Это заполнит строку в терминале, и курсор перейдет на другую строку, которая раздражает.

Я хотел бы вместо этого получить что-то вроде

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

Есть ли способ определить PS1переменную для «обтекания» и «сжатия» имени каталога, чтобы оно никогда не превышало определенного количества символов, получая более короткое приглашение?


1
К счастью, я помню, где я читал, как настроить приглашение оболочки: tldp.org/HOWTO/Bash-Prompt-HOWTO/x783.html Спасибо Джайлсу Орру, автору Bash Prompt HOWTO, и людям, которые внесли свой вклад.
состоялся

См. Также unix.stackexchange.com/a/216871/117549 (на основе ksh, но похожая идея)
Джефф Шаллер

Ответы:


16

Прежде всего, вы можете просто захотеть изменить \wс \W. Таким образом, печатается только имя текущего каталога, а не весь его путь:

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 $ 

Этого может быть недостаточно, если само имя каталога слишком длинное. В этом случае вы можете использовать PROMPT_COMMANDпеременную для этого. Это специальная переменная bash, значение которой выполняется в виде команды перед отображением каждого приглашения. Таким образом, если вы установите это для функции, которая устанавливает желаемое приглашение в зависимости от длины пути вашего текущего каталога, вы можете получить желаемый эффект. Например, добавьте эти строки в ваш ~/.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

Эффект выглядит так:

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

Добавление в символьное возвращение - мое главное решение

Так что мое приглашение (в котором есть и другие вещи, что делает его еще длиннее) выглядит так:

введите описание изображения здесь

Вы заметите, что $ возвращается как новая строка

Я достигаю этого с

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\$ '

Обратите внимание, что, хотя в отдельной строке, очень длинные деревья каталогов, такие как

/home/durrantm/Dropbox/96_2013_archive/work/code/ruby__rails сокращены до

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

т. е. «верхние 3 директории / _ / нижние две директории», что, как правило, меня волнует

Это гарантирует, что строка никогда не станет слишком длинной из-за длины дерева каталогов. Если вы всегда хотите полное дерево каталогов, просто настройте LOCATION, т.е.

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


1
Где ваша подсказка на самом деле включает $ ? (См. Это .)
G-Man говорит: «Восстановите Монику»

Добавил \ $ в конце, спасибо. это было потому, что обычно я также показываю свою ветку git, но здесь слишком много деталей
Майкл Даррант,

1
Какая? Нет новой строки? Нет SGR0?
G-Man говорит: «Восстановите Монику»

Добавлен перевод строки
Майкл Даррант

3

Создано ~ / .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\$ "

Добавлено в мой ~ / .bash_profile:

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

Выход:

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

1

Не решение для сокращения длинных путей, а удобный способ получить лучший обзор при сохранении всей видимой информации о пути - это добавление новой строки перед последним символом. Таким образом, курсор всегда начинается в одном и том же столбце, даже если путь достаточно длинный, чтобы его обернуть, но ваши окна консоли должны быть достаточно высокими, чтобы не слишком быстро прокручивать предыдущие строки. Я удалил цветовые коды для большей ясности:

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

1

Я использую это, он переносится на несколько строк и отступов по длине, user@hostпоэтому он предполагает, что ток PS1эффективно ' \u@\h:\w$'. Он не усекает путь и адаптируется к текущей ширине терминала. Он только разделяет путь /, поэтому элегантно не обрабатывает действительно длинные каталоги (но сохраняет места для выделения / копирования). Это гарантирует, что у вас всегда будет как минимум 20 символов для ввода.

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

Это работает, убирая магию \w(подходит только \w$для этого) PS1и заменяя ее $PWD, затем оборачивая ее в виде простой строки символов. Он пересчитывает PS1каждый раз исходное значение, которое сохраняется в нем _PS1, это означает, что также сохраняются "невидимые" экранированные символы, моя полная строка исходного приглашения xtermи приглашение, выделенное жирным шрифтом:

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

И конечный результат в терминале 80 столбцов:

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$ _

Это работает с Bash-3.2, как printf -v varиспользуется. Из-за различных сложностей потребуется некоторая корректировка для других вариаций PS1.

(Путь в строке заголовка xterm не оборачивается и не сокращается, что можно сделать, включив один из других ответов здесь в вышеуказанную функцию.)


0

В качестве альтернативы, в моем .zshrc я сокращаюсь до первой буквы каждого каталога, если ширина пикселя превышает определенную ширину:

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

будет выглядеть так:

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

Вот функция zsh для этого:

     # 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

Я на самом деле использую это, чтобы обновить заголовок терминала, чтобы с несколькими вкладками я мог точно определить, какая вкладка какая. Полный .zshrc для этого здесь .

Это очень удобно, поскольку сохраняет контекст и в zsh позволяет быстро заполнить каталог в том же формате. (например, при наборе cd /h/m/s/<tab>будет выполнено автозаполнение cd /home/mydirectory1/second_directory)


Как это относится к вопросу, поставленному OP, о том, как определить приглашение PS1?
Anthon

Отредактировано для ясности, $ t становится PS1
Ричард

0

Попробуйте использовать этот скрипт Python . Он обрезает отдельные части имени пути, точно так же, как вы хотели в вашем вопросе. Он также использует многоточие Юникода, которое занимает только один столбец вместо трех.

Пример вывода для вашего пути (если задано ограничение в 30 символов):

/home/mydir…/second…/my_actua

Стоит отметить, что это решение правильно обрабатывает Unicode в именах каталогов с помощью wcswidth. ${#PWD}, который использовали другие ответы, будет неправильно определять визуальную ширину любого пути, содержащего символы UTF-8.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.