Invite bash compacte lors de l'utilisation d'une arborescence de répertoires / nom de fichier

16

Dans un système avec Ubuntu 14.04 et bash, j'ai la PS1variable se terminant par le contenu suivant:

\u@\h:\w\$

afin que l'invite apparaisse comme

user@machinename:/home/mydirectory$

Parfois, cependant, le répertoire actuel a un nom long, ou il se trouve dans des répertoires avec des noms longs, de sorte que l'invite ressemble à

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

Cela remplira la ligne dans le terminal et le curseur ira sur une autre ligne, ce qui est ennuyeux.

J'aimerais plutôt obtenir quelque chose comme

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

Existe-t-il un moyen de définir la PS1variable pour "envelopper" et "compacter" le nom du répertoire, pour ne jamais dépasser un certain nombre de caractères, en obtenant une invite plus courte?

BowPark
la source
1
Heureusement, je me souviens où j'ai lu comment personnaliser l'invite du shell: tldp.org/HOWTO/Bash-Prompt-HOWTO/x783.html Merci à Giles Orr, auteur de Bash Prompt HOWTO et aux personnes qui y ont contribué.
stemd
Voir aussi unix.stackexchange.com/a/216871/117549 (basé sur ksh, mais idée similaire)
Jeff Schaller

Réponses:

16

Tout d'abord, vous voudrez peut-être simplement changer le \wavec \W. De cette façon, seul le nom du répertoire courant est imprimé et non son chemin complet:

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 $ 

Cela pourrait ne pas être suffisant si le nom du répertoire lui-même est trop long. Dans ce cas, vous pouvez utiliser la PROMPT_COMMANDvariable pour cela. Il s'agit d'une variable bash spéciale dont la valeur est exécutée sous forme de commande avant que chaque invite ne soit affichée. Donc, si vous définissez cela sur une fonction qui définit l'invite souhaitée en fonction de la longueur du chemin de votre répertoire actuel, vous pouvez obtenir l'effet que vous recherchez. Par exemple, ajoutez ces lignes à votre ~/.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

L'effet ressemble à ceci:

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 $ 
terdon
la source
10

Ajouter un retour de personnage est ma principale solution à cela

Donc mon invite (qui a aussi d'autres choses, ce qui la rend encore plus longue) ressemble à ceci:

entrez la description de l'image ici

Vous remarquerez que le $ est retourné en tant que nouvelle ligne

J'y parviens avec

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

Notez que, même si sur une ligne distincte, des arborescences de répertoires très longues telles que

/home/durrantm/Dropbox/96_2013_archive/work/code/ruby__rails sont raccourcis à

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

c'est-à-dire "les 3 principaux répertoires / _ / les deux derniers répertoires", ce qui m'intéresse généralement

Cela garantira que la ligne ne sera jamais trop longue en raison de la longueur de l'arborescence des répertoires. Si vous voulez toujours l'arborescence complète des répertoires, ajustez simplement LOCATION, c.-à-d.

LOCATION=' \[\033[01;34m\]`pwd`'
Michael Durrant
la source
Plus d'informations sur la couleur sur unix.stackexchange.com/q/124407/10043
Michael Durrant
1
Où votre invite inclut-elle réellement le $ ? (Voir ceci .)
G-Man dit 'Reinstate Monica'
Ajout du \ $ à la fin, merci. c'est parce que normalement je montre aussi ma branche git mais c'est trop de détails pour ici
Michael Durrant
1
Quelle? Pas de nouvelle ligne? Pas de SGR0?
G-Man dit `` Réintègre Monica '' le
Ajouté le saut de ligne
Michael Durrant
3

~ / .Bash_prompt créé:

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

Ajouté dans mon ~ / .bash_profile:

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

La sortie est:

user@machinename:/home/mydirectory1/...another_long_name$
hellcode
la source
1

Ce n'est pas une solution pour raccourcir les longs chemins, mais un moyen pratique d'obtenir une meilleure vue d'ensemble tout en gardant toutes les informations sur les chemins visibles, consiste à ajouter une nouvelle ligne avant le dernier caractère. De cette façon, le curseur démarre toujours dans la même colonne, même si le chemin est assez long pour passer, mais la ou les fenêtres de votre console doivent être suffisamment hautes pour ne pas faire défiler les lignes précédentes trop rapidement. J'ai supprimé les codes couleurs pour plus de clarté:

murphy@seasonsend:~
$ echo $PS1
\u@\h:\w\n\$
murphy@seasonsend:~
$ 
Murphy
la source
1

J'utilise cela, il s'enroule sur plusieurs lignes et indentations par la longueur de user@hostsorte qu'il suppose que le courant PS1est effectivement ' \u@\h:\w$'. Il ne tronque pas le chemin et s'adapte à la largeur de terminal actuelle. Il ne fait que diviser le chemin /, donc il ne traite pas avec élégance les très longs répertoires (mais il conserve des espaces pour la sélection / copie). Il s'assure que vous avez toujours au moins un espace de 20 caractères disponibles pour la saisie.

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

Cela fonctionne en retirant la magie \w(correspond uniquement \w$à cela) PS1et en la remplaçant $PWD, puis en l'enveloppant comme une simple chaîne de caractères. Il recalcule à PS1chaque fois à partir de la valeur d'origine qui est enregistrée _PS1, cela signifie que les échappements "invisibles" sont également préservés, ma chaîne d'invite d'origine complète xtermet l'invite en gras:

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

Et le résultat final dans un terminal de 80 colonnes:

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

Cela fonctionne à partir de bash-3.2 tel qu'il printf -v varest utilisé. En raison de diverses complexités, il faudra un certain ajustement pour d'autres variations de PS1.

(Le chemin dans la barre de titre xterm n'est ni encapsulé ni abrégé, ce qui pourrait être fait en incorporant l'une des autres réponses ici dans la fonction ci-dessus.)

Mr Spuratic
la source
0

Comme alternative, dans mon .zshrc j'abrège la première lettre de chaque répertoire si la largeur de pixel est supérieure à une certaine largeur:

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

devient:

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

Voici la fonction zsh pour le faire:

     # 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

En fait, je l'utilise pour mettre à jour le titre du terminal afin qu'avec plusieurs onglets, je puisse garder directement quel onglet est lequel. Le fichier .zshrc complet pour ce faire est ici .

C'est très pratique car il garde le contexte et en zsh vous permet de tabuler rapidement un répertoire dans le même format. (par exemple, la saisie se cd /h/m/s/<tab>terminera automatiquement cd /home/mydirectory1/second_directory)

Richard
la source
Comment cela s'applique-t-il à la question posée par l'OP, sur la façon de définir l'invite PS1?
Anthon
Modifié pour plus de clarté, $ t devient PS1
Richard
0

Essayez d'utiliser ce script Python . Il coupe les sections individuelles du nom du chemin, exactement comme vous le vouliez dans votre question. Il utilise également les points de suspension unicode qui occupent une seule colonne au lieu de trois.

Exemple de sortie pour votre chemin (quand on lui donne une limite de 30 caractères):

/home/mydir…/second…/my_actua

Il convient de noter que cette solution gère correctement Unicode dans les noms de répertoire à l'aide de wcswidth. ${#PWD}, que d'autres réponses ont utilisé, va mal évaluer la largeur visuelle de tout chemin contenant des caractères UTF-8.

Functino
la source