Подтвердить что ты не робот

Bash Сокращение PWD

Я ищу функцию 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 файла.

4b9b3361

Ответ 1

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

(я начал писать это в простой оболочке script, но man, 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

Не дает тот же результат, но мой ~/.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='\[email protected]\h:$(_PS1 "$PWD" 20)\$ '

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

Ответ 3

Здесь 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 *" - это столько же символов, сколько "дом" ).

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

Ответ 4

FYI, есть встроенный \w "shortener" в Bash 4 +:

PROMPT_DIRTRIM=3

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

Ответ 5

Я внес некоторые улучшения в код Evan Krall. Теперь он проверяет, начинается ли ваш путь в $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, но до сих пор у меня не было проблем со случайным использованием. Обратите внимание, что я сохранил выше script как "shortdir" и поместил его в свой PATH.

function tinypwd(){
    PWD=`shortdir`
}

function hugepwd(){
    PWD=`pwd`
}

EDIT 19 октября 2010

Правильный способ выполнения псевдонимов в bash заключается в изменении переменной $PS1; так анализируется подсказка. В случаях MOST (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') "                            
} 

Ответ 6

Здесь относительно простое решение perl. Это короткий достаточно, чтобы вы могли встроить его непосредственно в PS1 чем вызов script. Он дает все символы усеченных имен вместо замены на..

$ 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/(.)(.*)/;
 $_ = $1 . "." x (length $2 > 2 ? 2 : length $2 ) if $i++ < @F - 2; $_ } @F)'
/t../i./a/r../l../p../i./like/shortened

Ответ 7

Здесь другой ответ на Эван:

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.
}

Загрузите script здесь и включите его в свой .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)\$ "