2009-10-24 9 views
19

Estoy buscando una función bash que acorte los nombres de las rutas largas para evitar que mi variable PS1 se alargue demasiado. Algo a lo largo de las líneas de:Bash PWD Shortening

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

podría terminar como:

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

algo que el tomó el camino y un número máximo aceptable de caracteres para acortar a que sería perfecto para mi archivo .bashrc.

+3

Personalmente, tengo el prompt acaba de ser los dos directorios superiores, por lo que lo anterior se convertiría en 'li ke/acortado'. Yo uso ZSH, sin embargo, así que no sé cómo lo harías en bash. – pavpanchekha

+1

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

+1

Y para usar en PS1, uno podría: función pwd_depth_limit_2 { if [" $ PWD "=" $ HOME "] then echo -n" ~ " else pwd | sed -e "s |. */\ (. * /. * \) | \ 1 |" fi } – polypus74

Respuesta

4

¿Qué tal una secuencia de comandos de Python? Esto acorta primero los nombres de directorio más largos, un carácter a la vez hasta que alcanza su objetivo de longitud o no puede acortar el camino. No acorta el último directorio en la ruta.

(Empecé a escribir esto en shell script sencillo, pero el hombre, golpe del apesta en la manipulación de cadenas.)

#!/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 

Ejemplo de salida:

$ 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

Estaba escribiendo un script de python que es bastante similar a este. El mío tiene un poco más de recursividad, es un poco más eficiente y no logra detener el truncamiento cuando se alcanza la longitud deseada. Por lo tanto, no voy a molestarme en terminar y publicarlo a menos que a alguien le importe. : -/ – Benson

+0

Agradable. Mi única preocupación es el costo de ejecutar una secuencia de comandos python en cada ejecución de shell. Lo intentaré y te lo haré saber. –

+0

Si es demasiado lento, hágamelo saber, sin duda se puede hacer más rápido si es necesario. –

14

Aquí hay una solución fiesta, sólo que te pueden gustar . Esto acorta cada parte de la ruta hasta el prefijo más corto que aún puede completarse con pestañas, y usa * en lugar de ... como relleno.

#!/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. 

Dale la longitud como primer argumento, y la ruta como el segundo argumento opcional. Si no se proporciona un segundo argumento, usa el directorio de trabajo actual.

Esto intentará acortar por debajo de la longitud indicada. Si eso no es posible, solo da el camino más corto que puede dar.

Algorítmicamente hablando, esto es probablemente horrible, pero termina siendo bastante rápido. (La clave para los guiones de shell rápidos es evitar subcapas y comandos externos, especialmente en los bucles internos.)

Por diseño, solo se acorta en 2 o más caracteres ('hom *' tiene tantos caracteres como 'inicio') .

No es perfecto. Hay algunas situaciones en las que no se acortará tanto como sea posible, como si hay varios archivos cuyos nombres de archivos comparten un prefijo (si foobar1 y foobar2 existen, foobar3 no se acortará.)

+4

Me gusta la idea de mostrar el prefijo único que puede tabularse. –

1

Aquí es relativamente fácil solución perl. Esto es tan corto como que podría incrustarlo directamente en PS1 en lugar de que invocar un script. Proporciona todos los caracteres de los nombres truncados en lugar de reemplazarlos por '.' ''

 

$ 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 

que no estoy viendo inmediatamente una buena manera de reemplazar los caracteres con, pero aquí es una manera fea:

 

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

Gracias por esto. Lo tomé prestado para sugerir una respuesta a (casi) la misma pregunta sobre el Super Usuario. http://superuser.com/questions/180257/bash-prompt-how-to-have-the-initials-of-directory-path – Telemachus

34

no da el mismo resultado, pero mi ~/.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)\$ ' 

que limita la ruta mostrada a 20 caracteres máx. Si la ruta tiene más de 20 caracteres, se mostrará como /...d/like/shortened o ~/.../like/shortened.

+1

Esto es genial. Simple y efectivo. –

+1

Esta es claramente la respuesta correcta. ¡Gracias! – nibot

9

Realicé algunas mejoras en el código de Evan Krall. Ahora se comprueba para ver si su ruta comienza en $ HOME y comienza la variedad de corta duración con ~/en lugar de/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. 

nuevo, aquí hay algunas funciones que puse en mi archivo .bashrc a reducir el camino que muestra el caparazón. No estoy seguro si editar $ PWD como este es completamente seguro ya que algunos scripts pueden depender de una cadena de $ PWD válida, pero hasta ahora no he tenido problemas con el uso ocasional. Tenga en cuenta que guardé el script anterior como "shortdir" y lo puse en mi PATH.

function tinypwd(){ 
    PWD=`shortdir` 
} 

function hugepwd(){ 
    PWD=`pwd` 
} 

EDITAR 19 de Oct 2010

La forma correcta de hacer los alias en bash es mediante la modificación de la variable $PS1; así es como se analiza el mensaje. En la mayoría de los casos (99% del tiempo), la ruta actual está en la cadena de solicitud como "\ w". Podemos usar sed para reemplazar esto con shortdir, así:

#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

FYI, hay un built-in \w "acortador" en Bash 4+:

PROMPT_DIRTRIM=3 

acortará /var/lib/whatever/foo/bar/baz a .../foo/bar/baz.

+2

Genial; solo para aclarar: requiere bash v4 + (OS X 10.8, por ejemplo, viene con bash 3.2.48). – mklement0

+0

@mklement: corregido, gracias –

1

Aquí hay otro giro en la respuesta de Evan:

enter image description here

Ésta utiliza más (+) en lugar de un asterisco (*) para las rutas truncados. Reemplaza la ruta HOME con ~, y deja el segmento de directorio final intacto. Si el segmento final tiene más de 20 caracteres, lo reduce al bit de tabulación completa y agrega puntos suspensivos (...).

#!/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. 
} 

Descargar el guión aquí e incluirlo en su .bashrc:

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

. ~/.bash_scripts/pwd-prompt.bash 

agregar el directorio a su PS1 así:

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