Prompt compacto do bash ao usar uma árvore de diretórios / nome de arquivo


16

Em um sistema com o Ubuntu 14.04 e bash, tenho a PS1variável terminando com o seguinte conteúdo:

\u@\h:\w\$

para que o prompt apareça como

user@machinename:/home/mydirectory$

Às vezes, no entanto, o diretório atual tem um nome longo ou está dentro de diretórios com nomes longos, de modo que o prompt se parece com

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

Isso preencherá a linha no terminal e o cursor irá para outra linha, o que é irritante.

Em vez disso, gostaria de obter algo como

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

Existe uma maneira de definir a PS1variável para "quebrar" e "compactar" o nome do diretório, para nunca exceder um certo número de caracteres, obtendo um prompt mais curto?


11
Felizmente, lembro onde li como personalizar o prompt do shell: tldp.org/HOWTO/Bash-Prompt-HOWTO/x783.html Obrigado por Giles Orr, autor do Bash Prompt HOWTO e pelas pessoas que contribuíram para ele.
stemd

Veja também unix.stackexchange.com/a/216871/117549 (idéia baseada em ksh, mas similar)
Jeff Schaller

Respostas:


16

Primeiro de tudo, você pode simplesmente querer mudar o \wcom \W. Dessa forma, apenas o nome do diretório atual é impresso e não o caminho inteiro:

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 $ 

Isso ainda pode não ser suficiente se o próprio nome do diretório for muito longo. Nesse caso, você pode usar a PROMPT_COMMANDvariável para isso. Essa é uma variável bash especial cujo valor é executado como um comando antes de cada prompt ser mostrado. Portanto, se você definir isso como uma função que define o prompt desejado com base no comprimento do caminho do diretório atual, poderá obter o efeito que deseja. Por exemplo, adicionar essas linhas ao seu ~/.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

O efeito é assim:

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

Adicionar um retorno de personagem é minha principal solução para isso.

Portanto, meu prompt (que tem outras coisas também, tornando-o ainda mais longo) fica assim:

insira a descrição da imagem aqui

Você notará que o $ está sendo retornado como uma nova linha

Eu alcanço isso com

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

Observe que, embora em uma linha separada, árvores de diretório super longas, como

/home/durrantm/Dropbox/96_2013_archive/work/code/ruby__rails são encurtados para

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

ou seja, "três principais diretórios / _ / dois diretórios inferiores", que é geralmente o que me interessa

Isso garantirá que a linha nunca fique muito longa devido ao comprimento da árvore de diretórios. Se você sempre deseja a árvore de diretórios completa, basta ajustar LOCATION, ou seja,

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


11
Onde seu prompt realmente inclui o $ ? (Veja isso .)
G-Man Diz 'Reinstate Monica'

Adicionado o \ $ no final, obrigado. foi porque normalmente eu também mostro meu ramo git, mas isso é um detalhe demais para aqui #
Michael Durrant

11
O que? Não há nova linha? Sem SGR0?
G-Man diz 'Reinstate Monica'

Adicionado o linefeed
Michael Durrant

3

Criado ~ / .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\$ "

Adicionado no meu ~ / .bash_profile:

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

A saída é:

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

1

Não é uma solução para encurtar caminhos longos, mas uma maneira conveniente de obter uma melhor visão geral, mantendo todas as informações do caminho visíveis, é adicionar uma nova linha antes do último caractere. Dessa forma, o cursor começa sempre na mesma coluna, mesmo que o caminho seja longo o suficiente para contornar, mas a (s) janela (s) do console devem ser altas o suficiente para não rolar as linhas anteriores muito rapidamente. Eu removi os códigos de cores para obter mais clareza:

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

1

Eu uso isso, ele envolve várias linhas e recuos pelo comprimento de user@hostmodo que assume que a corrente PS1é efetivamente ' \u@\h:\w$'. Não trunca o caminho e se adapta à largura atual do terminal. Ele apenas divide o caminho /, portanto, não lida com diretórios realmente longos (mas preserva espaços para seleção / cópia). Isso garante que você sempre tenha pelo menos um espaço de 20 caracteres disponível para entrada.

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

Isso funciona retirando e substituindo a mágica \w( substituindo-a apenas \w$por isso) PS1e $PWD, em seguida, agrupando-a como uma sequência simples de caracteres. Ele recalcula PS1cada vez que o valor original é salvo _PS1, isso significa que escapes "invisíveis" também são preservados, minha string de prompt original completa para xterme prompt em negrito:

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

E o resultado final em um terminal de 80 colunas:

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

Isso funciona do bash-3.2 conforme printf -v varé usado. Devido a várias complexidades , será necessário algum ajuste para outras variações de PS1.

(O caminho na barra de título do xterm não é encapsulado nem abreviado, algo que pode ser feito incorporando uma das outras respostas aqui à função acima.)


0

Como alternativa, no meu arquivo .zshrc, abrevio a primeira letra de cada diretório se a largura do pixel estiver acima de uma certa largura:

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

torna-se:

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

Aqui está a função zsh para fazer isso:

     # 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

Na verdade, eu uso isso para atualizar o título do terminal para que, com várias guias, eu possa manter em linha reta qual é qual. O .zshrc completo para fazer isso está aqui .

Isso é muito útil, pois mantém o contexto e no zsh permite que você rapidamente complete um diretório no mesmo formato. (por exemplo, a digitação cd /h/m/s/<tab>será concluída automaticamente cd /home/mydirectory1/second_directory)


Como isso se aplica à pergunta do OP, sobre como definir o prompt do PS1?
Anthon 28/02

Editado por questões de clareza, $ t se torna PS1 #
Richard Richard

0

Tente usar este script Python . Ele corta seções individuais do nome do caminho, exatamente como você queria na sua pergunta. Ele também usa as reticências unicode que ocupam apenas uma coluna em vez de três.

Exemplo de saída para o seu caminho (quando recebe um limite de 30 caracteres):

/home/mydir…/second…/my_actua

Vale a pena notar que esta solução manipula corretamente Unicode em nomes de diretório usando wcswidth. ${#PWD}, que outras respostas usaram, avaliarão mal a largura visual de qualquer caminho que contenha caracteres UTF-8.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.