2009-10-24 10 views
19

Sto cercando una funzione bash che ridurrà i nomi di percorso lunghi per mantenere la mia variabile PS1 troppo lunga. Qualcosa sulla falsariga di:Bash PWD Shortening

/this/is/the/path/to/a/really/long/directory/i/would/like/shortened 

potrebbe finire come:

/t../i../t../p../to/a/r../l../d../i/w../like/shortened 

qualcosa che l'ha preso il percorso e un numero massimo accettabile di caratteri per accorciare al sarebbe perfetto per il mio file .bashrc.

+3

Personalmente, ho la prompt solo essere le prime due directory, quindi quanto sopra sarebbe diventato 'li ke/shortened'. Io uso ZSH, però, quindi non so come lo faresti in bash. – pavpanchekha

+1

@pavpanchekha 'pwd | sed -e "s |. */\ (. * /. * \) | \ 1 |" ' – polypus74

+1

E per l'utilizzo in PS1, uno potrebbe: function pwd_depth_limit_2 { if [" $ PWD "=" $ HOME "] then echo -n" ~ " else pwd | sed -e "s |. */\ (. * /. * \) | \ 1 |" fi } – polypus74

risposta

4

Che ne dici di uno script Python? Questo accorcia prima i nomi delle directory più lunghi, un carattere alla volta finché non raggiunge il suo obiettivo di lunghezza o non riesce a ottenere il percorso più corto. Non accorcia l'ultima directory nel percorso.

(ho iniziato a scrivere questo script di shell semplice ma l'uomo, bash puzza di manipolazione delle stringhe.) Uscita

#!/usr/bin/env python 
import sys 

try: 
    path = sys.argv[1] 
    length = int(sys.argv[2]) 
except: 
    print >>sys.stderr, "Usage: $0 <path> <length>" 
    sys.exit(1) 

while len(path) > length: 
    dirs = path.split("/"); 

    # Find the longest directory in the path. 
    max_index = -1 
    max_length = 3 

    for i in range(len(dirs) - 1): 
     if len(dirs[i]) > max_length: 
      max_index = i 
      max_length = len(dirs[i]) 

    # Shorten it by one character.  
    if max_index >= 0: 
     dirs[max_index] = dirs[max_index][:max_length-3] + ".." 
     path = "/".join(dirs) 

    # Didn't find anything to shorten. This is as good as it gets. 
    else: 
     break 

print path 

Esempio:

$ echo $DIR 
/this/is/the/path/to/a/really/long/directory/i/would/like/shortened 
$ ./shorten.py $DIR 70 
/this/is/the/path/to/a/really/long/directory/i/would/like/shortened 
$ ./shorten.py $DIR 65 
/this/is/the/path/to/a/really/long/direc../i/would/like/shortened 
$ ./shorten.py $DIR 60 
/this/is/the/path/to/a/re../long/di../i/would/like/shortened 
$ ./shorten.py $DIR 55 
/t../is/the/p../to/a/r../l../di../i/wo../like/shortened 
$ ./shorten.py $DIR 50 
/t../is/the/p../to/a/r../l../d../i/w../l../shortened 
+0

Stavo solo scrivendo uno script python che è abbastanza simile a questo. Il mio fa un po 'più di ricorsione, è leggermente più efficiente e non riesce a smettere di troncare quando viene raggiunta la lunghezza desiderata. Pertanto, non mi preoccuperò di finirlo e postarlo a meno che qualcuno non si preoccupi. : -/ – Benson

+0

Bello. La mia unica preoccupazione è il costo dell'esecuzione di uno script python su ogni esecuzione della shell. Farò un tentativo e farti sapere. –

+0

Se è troppo lento fammi sapere, può certamente essere reso più veloce se necessario. –

14

Ecco una soluzione bash solo che ti avrebbe fatto piacere . Questo riduce ogni parte del percorso fino al prefisso più breve che può ancora essere completato da tabulazioni e utilizza * invece di .. come riempitivo.

#!/bin/bash 

begin="" # The unshortened beginning of the path. 
shortbegin="" # The shortened beginning of the path. 
current="" # The section of the path we're currently working on. 
end="${2:-$(pwd)}/" # The unmodified rest of the path. 
end="${end#/}" # Strip the first/
shortenedpath="$end" # The whole path, to check the length. 
maxlength="${1:-0}" 

shopt -q nullglob && NGV="-s" || NGV="-u" # Store the value for later. 
shopt -s nullglob # Without this, anything that doesn't exist in the filesystem turns into */*/*/... 

while [[ "$end" ]] && ((${#shortenedpath} > maxlength)) 
do 
    current="${end%%/*}" # everything before the first/
    end="${end#*/}" # everything after the first/

    shortcur="$current" 
    shortcurstar="$current" # No star if we don't shorten it. 

    for ((i=${#current}-2; i>=0; i--)) 
    do 
    subcurrent="${current:0:i}" 
    matching=("$begin/$subcurrent"*) # Array of all files that start with $subcurrent. 
    ((${#matching[*]} != 1)) && break # Stop shortening if more than one file matches. 
    shortcur="$subcurrent" 
    shortcurstar="$subcurrent*" 
    done 

    begin="$begin/$current" 
    shortbegin="$shortbegin/$shortcurstar" 
    shortenedpath="$shortbegin/$end" 
done 

shortenedpath="${shortenedpath%/}" # strip trailing/
shortenedpath="${shortenedpath#/}" # strip leading/

echo "/$shortenedpath" # Make sure it starts with/

shopt "$NGV" nullglob # Reset nullglob in case this is being used as a function. 

Assegnare la lunghezza come primo argomento e il percorso come secondo argomento opzionale. Se non viene fornito un secondo argomento, utilizza la directory di lavoro corrente.

Questo cercherà di accorciare in base alla lunghezza indicata. Se ciò non è possibile, dà solo il percorso più breve che può dare.

Algoritmicamente parlando, questo è probabilmente orribile, ma finisce per essere piuttosto veloce. (La chiave per gli script rapidi della shell è evitare sottotitoli e comandi esterni, specialmente nei loop interni.)

In base alla progettazione, si accorcia solo di 2 o più caratteri ('hom *' è uguale a tanti caratteri di 'casa') .

Non è perfetto. Ci sono alcune situazioni in cui non si accorcia il più possibile, come se ci fossero diversi file i cui nomi di file condividono un prefisso (Se esistono foobar1 e foobar2, foobar3 non sarà abbreviato.)

+4

Mi piace l'idea di visualizzare il prefisso univoco che è tab-completabile. –

1

Ecco un relativamente facile soluzione perl. Questo è breve abbastanza da poter essere incorporato direttamente in PS1 piuttosto piuttosto che richiamare uno script. Fornisce tutti i caratteri dei nomi troncati anziché sostituire con "." ''

 

$ echo '/this/is/a/realy/long/path/id/like/shortened' | 
perl -F/ -ane 'print join("/", map { $i++ < @F - 2 ? 
substr $_,0,3 : $_ } @F)' 
/thi/is/a/rea/lon/pat/id/like/shortened 

io non sto vedendo subito un bel modo per sostituire i caratteri con, ma ecco un modo brutto:

 

echo '/this/is/a/realy/long/path/id/like/shortened' | 
perl -F/ -ane 'print join("/", map { m/(.)(.*)/; 
$_ = $1 . "." x (length $2 > 2 ? 2 : length $2) if $i++ < @F - 2; $_ } @F)' 
/t../i./a/r../l../p../i./like/shortened 
+0

Grazie per questo. L'ho preso in prestito per suggerire una risposta (più o meno) alla stessa domanda su Super User. http://superuser.com/questions/180257/bash-prompt-how-to-have-the-initials-of-directory-path – Telemachus

34

non dà lo stesso risultato, ma il mio ~/.bashrc contiene

_PS1() 
{ 
    local PRE= NAME="$1" LENGTH="$2"; 
    [[ "$NAME" != "${NAME#$HOME/}" || -z "${NAME#$HOME}" ]] && 
     PRE+='~' NAME="${NAME#$HOME}" LENGTH=$[LENGTH-1]; 
    ((${#NAME}>$LENGTH)) && NAME="/...${NAME:$[${#NAME}-LENGTH+4]}"; 
    echo "$PRE$NAME" 
} 
PS1='\[email protected]\h:$(_PS1 "$PWD" 20)\$ ' 

che limita il percorso visualizzato a 20 caratteri max. Se il percorso ha più di 20 caratteri, verrà mostrato come /...d/like/shortened o ~/.../like/shortened.

+1

Questo è fantastico. Semplice ed efficace. –

+1

Questa è chiaramente la risposta corretta. Grazie! – nibot

9

Ho apportato alcuni miglioramenti al codice di Evan Krall. E 'ora controlla per vedere se il percorso inizia in $ HOME e inizia la varietà accorciato con ~/invece di/h */u */

#!/bin/bash 

begin="" # The unshortened beginning of the path. 
shortbegin="" # The shortened beginning of the path. 
current="" # The section of the path we're currently working on. 
end="${2:-$(pwd)}/" # The unmodified rest of the path. 

if [[ "$end" =~ "$HOME" ]]; then 
    INHOME=1 
    end="${end#$HOME}" #strip /home/username from start of string 
    begin="$HOME"  #start expansion from the right spot 
else 
    INHOME=0 
fi 

end="${end#/}" # Strip the first/
shortenedpath="$end" # The whole path, to check the length. 
maxlength="${1:-0}" 

shopt -q nullglob && NGV="-s" || NGV="-u" # Store the value for later. 
shopt -s nullglob # Without this, anything that doesn't exist in the filesystem turns into */*/*/... 

while [[ "$end" ]] && ((${#shortenedpath} > maxlength)) 
do 
    current="${end%%/*}" # everything before the first/
    end="${end#*/}" # everything after the first/

    shortcur="$current" 
    shortcurstar="$current" # No star if we don't shorten it. 

    for ((i=${#current}-2; i>=0; i--)); do 
    subcurrent="${current:0:i}" 
    matching=("$begin/$subcurrent"*) # Array of all files that start with $subcurrent. 
    ((${#matching[*]} != 1)) && break # Stop shortening if more than one file matches. 
    shortcur="$subcurrent" 
    shortcurstar="$subcurrent*" 
    done 

    #advance 
    begin="$begin/$current" 
    shortbegin="$shortbegin/$shortcurstar" 
    shortenedpath="$shortbegin/$end" 
done 

shortenedpath="${shortenedpath%/}" # strip trailing/
shortenedpath="${shortenedpath#/}" # strip leading/

if [ $INHOME -eq 1 ]; then 
    echo "~/$shortenedpath" #make sure it starts with ~/ 
else 
    echo "/$shortenedpath" # Make sure it starts with/
fi 

shopt "$NGV" nullglob # Reset nullglob in case this is being used as a function. 

Inoltre, qui ci sono alcune funzioni che ho messo nel mio file .bashrc a riduci il percorso mostrato dalla conchiglia. Non sono sicuro che la modifica di $ PWD come questa sia completamente sicura poiché alcuni script potrebbero dipendere da una stringa $ PWD valida, ma finora non ho avuto problemi con l'uso occasionale. Nota che ho salvato lo script di cui sopra come "shortdir" e lo metto nel mio PATH.

function tinypwd(){ 
    PWD=`shortdir` 
} 

function hugepwd(){ 
    PWD=`pwd` 
} 

EDIT 19 ott 2010

Il modo corretto di fare gli alias in bash è modificando la variabile $PS1; questo è il modo in cui viene analizzato il prompt. Nei casi MOST (99% delle volte) il percorso corrente si trova nella stringa di richiesta come "\ w". Possiamo usare sed per sostituire questo con shortdir, in questo modo:

#NOTE: trailing space before the closing double-quote (") is a must!! 
function tinypwd(){                
    PS1="$(echo $PS1 | sed 's/\\w/\`shortdir\`/g') " 
}                    

function hugepwd(){                
    PS1="$(echo $PS1 | sed 's/[`]shortdir[`]/\\w/g') "        
} 
11

cronaca, c'è un built-in \w "shortener" in Bash 4+:

PROMPT_DIRTRIM=3 

accorcerà /var/lib/whatever/foo/bar/baz-.../foo/bar/baz.

+2

Grande; solo per chiarire: richiede bash v4 + (OS X 10.8, per esempio, viene fornito con bash 3.2.48). – mklement0

+0

@mklement: corretto, grazie –

1

Ecco un altro giro sulla risposta di Evan:

enter image description here

Questo si usa più (+) al posto di un asterisco (*) per i percorsi troncati. Sostituisce il percorso HOME con ~, e lascia intatto il segmento finale della directory. Se il segmento finale ha più di 20 caratteri, lo accorcia al bit completabile dalla scheda e aggiunge un ellisso (...).

#!/bin/bash 
# Modified from http://stackoverflow.com/a/1617048/359287 
# By Alan Christopher Thomas (http://alanct.com) 

__pwd_ps1() 
{ 
    begin="" 
    homebegin="" 
    shortbegin="" 
    current="" 
    end="${2:-$(pwd)}/" # The unmodified rest of the path. 
    end="${end#/}" # Strip the first/
    shortenedpath="$end" 

    shopt -q nullglob && NGV="-s" || NGV="-u" 
    shopt -s nullglob 

    while [[ "$end" ]] 
    do 
     current="${end%%/*}" # Everything before the first/
     end="${end#*/}" # Everything after the first/

     shortcur="$current" 
     for ((i=${#current}-2; i>=0; i--)) 
     do 
     [[ ${#current} -le 20 ]] && [[ -z "$end" ]] && break 
     subcurrent="${current:0:i}" 
     matching=("$begin/$subcurrent"*) # Array of all files that start with $subcurrent 
     ((${#matching[*]} != 1)) && break # Stop shortening if more than one file matches 
     [[ -z "$end" ]] && shortcur="$subcurrent..." # Add character filler at the end of this string 
     [[ -n "$end" ]] && shortcur="$subcurrent+" # Add character filler at the end of this string 
     done 

     begin="$begin/$current" 
     homebegin="$homebegin/$current" 
     [[ "$homebegin" =~ ^"$HOME"(/|$) ]] && homebegin="~${homebegin#$HOME}" # Convert HOME to ~ 
     shortbegin="$shortbegin/$shortcur" 
     [[ "$homebegin" == "~" ]] && shortbegin="~" # Use ~ for home 
     shortenedpath="$shortbegin/$end" 
    done 

    shortenedpath="${shortenedpath%/}" # Strip trailing/
    shortenedpath="${shortenedpath#/}" # Strip leading/

    [[ ! "$shortenedpath" =~ ^"~" ]] && printf "/$shortenedpath" # Make sure it starts with/
    [[ "$shortenedpath" =~ ^"~" ]] && printf "$shortenedpath" # Don't use/for home dir 

    shopt "$NGV" nullglob # Reset nullglob in case this is being used as a function. 
} 

scaricare lo script qui e includerlo nel .bashrc:

https://raw.github.com/alanctkc/dotfiles/master/.bash_scripts/pwd-prompt.bash

. ~/.bash_scripts/pwd-prompt.bash 

Aggiungere la directory al vostro PS1 in questo modo:

export PS1="[other stuff...] \$(__pwd_ps1)\$ " 
Problemi correlati