Bash PWD Укорочение - PullRequest
       9

Bash PWD Укорочение

21 голосов
/ 24 октября 2009

Я ищу функцию bash, которая сокращает длинные имена путей, чтобы моя переменная PS1 не получала слишком длинные. Что-то вроде:

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

может закончиться как:

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

что-то, что взял путь и максимально допустимое количество символов, чтобы сократить, было бы идеально для моего файла .bashrc.

Ответы [ 8 ]

34 голосов
/ 24 октября 2009

Не дает того же результата, но мой ~/.bashrc содержит

_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='\u@\h:$(_PS1 "$PWD" 20)\$ '

, который ограничивает отображаемый путь до 20 символов. Если путь превышает 20 символов, он будет показан как /...d/like/shortened или ~/.../like/shortened.

15 голосов
/ 24 октября 2009

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

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

Дайте ему длину в качестве первого аргумента и путь в качестве необязательного второго аргумента. Если второй аргумент не указан, используется текущий рабочий каталог.

Это попытается сократить до указанной длины. Если это невозможно, он просто дает кратчайший путь, который он может дать.

С точки зрения алгоритма, это, вероятно, ужасно, но в итоге получается довольно быстро. (Ключ к быстрым сценариям оболочки - избегать подоболочек и внешних команд, особенно во внутренних циклах.)

По замыслу он укорачивается только на 2 или более символов («hom *» - это столько же символов, сколько «home»).

Это не идеально. В некоторых ситуациях он не будет сокращен настолько, насколько это возможно, например, если есть несколько файлов, имена файлов которых имеют префикс (если существуют foobar1 и foobar2, foobar3 не будет сокращен.)

11 голосов
/ 16 апреля 2013

К вашему сведению, в Bash 4 + есть встроенный \w «укорочитель»:

PROMPT_DIRTRIM=3

сократит /var/lib/whatever/foo/bar/baz до .../foo/bar/baz.

9 голосов
/ 01 июня 2010

Я сделал некоторые улучшения в коде Эвана Кралла. Теперь он проверяет, начинается ли ваш путь в $ HOME и начинается ли сокращенный сорт с ~ / вместо / 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.

Кроме того, вот некоторые функции, которые я добавил в свой файл .bashrc, чтобы уменьшить путь, показанный оболочкой. Я не уверен, что редактирование $ PWD, как это, полностью безопасно, поскольку некоторые сценарии могут зависеть от корректной строки $ PWD, но до сих пор у меня не было проблем со случайным использованием. Обратите внимание, что я сохранил приведенный выше скрипт как «shortdir» и поместил его в свой путь PATH.

function tinypwd(){
    PWD=`shortdir`
}

function hugepwd(){
    PWD=`pwd`
}

РЕДАКТИРОВАТЬ 19 октября 2010

Правильный способ сделать псевдонимы в bash - изменить переменную $PS1; Вот как анализируется приглашение. В большинстве случаев (99% времени) текущий путь указывается в строке приглашения как «\ w». Мы можем использовать sed, чтобы заменить это на shortdir, вот так:

#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') "                            
} 
5 голосов
/ 24 октября 2009

Как насчет скрипта Python? Это сокращает сначала самые длинные имена каталогов, по одному символу за раз, пока он не достигнет своей цели длины или не сможет получить путь короче. Это не сокращает последний каталог в пути.

(я начал писать это простым сценарием оболочки, но человек, bash воняет при манипулировании строками.)

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

Пример вывода:

$ 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
2 голосов
/ 24 октября 2013

Вот еще один ответ Эвана:

enter image description here

Этот использует плюс (+) вместо звездочки (*) для усеченных путей. Он заменяет путь HOME на ~ и оставляет последний сегмент каталога без изменений. Если последний сегмент содержит более 20 символов, он сокращает его до бита, завершаемого табуляцией, и добавляет эллипсы (...).

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

Загрузите скрипт здесь и включите его в свой .bashrc:

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

. ~/.bash_scripts/pwd-prompt.bash

Добавьте каталог к ​​вашему PS1 так:

export PS1="[other stuff...] \$(__pwd_ps1)\$ "
1 голос
/ 24 октября 2009

Вот относительно простое решение Perl. Это коротко Достаточно того, что вы могли бы встроить его непосредственно в PS1, а чем вызывать скрипт. Это дает все символы сокращенных имен вместо замены на '.'


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

Я не сразу вижу хороший способ заменить символы на «.», но вот уродливый способ:


echo '/this/is/a/realy/long/path/id/like/shortened' |
 perl -F/ -ane 'print join( "/", map { m/(.)(.&#42;)/;
 $&#95; = $1 . "." x (length $2 &#62; 2 ? 2 : length $2 ) if $i++ &#60; @F - 2; $&#95; } @F)'
/t../i./a/r../l../p../i./like/shortened
0 голосов
/ 26 мая 2018

Попробуйте это:

PS1='$(pp="$PWD/" q=${pp/#"$HOME/"/} p=${q%?};((${#p}>19))&&echo "${p::9}…${p:(-9)}"||echo "$p") \$'

Преобразует

~/.vim/bundle/ack.vim/plugin

до

.vim/bund…im/plugin

transfrom

/usr/share/doc/xorg-x11-font-utils-7.5/

до

/usr/shar…utils-7.5

А когда $PWD совпадает с $HOME, ничего не показывать.

Бонус: вы можете изменить количество длины в соответствии с вашими потребностями.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...