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

Задача кода: Bash приглашение на короткий путь

Я применил кратчайший путь для bash, который будет включен в переменную среды PS1, что сокращает рабочий каталог до более компактного, но все же описательного. Мне любопытно, какие существуют другие идеи.

Здесь проблема:

Создайте функцию bash _dir_chomp, которая может быть включена в PS1, как это (разрывы строк вставлены для удобочитаемости):

PS1='\[\033[01;32m\]\[email protected]\h\[\033[01;34m\] $(
  _dir_chomp "$(pwd)" 20
)\[\033[01;37m\]$(parse_git_branch)\[\033[01;34m\] \$\[\033[00m\] '

с параметром "20", являющимся параметром максимальной длины в качестве мягкого предела. Вот примеры:

  • /usr/portage/media-plugins/banshee-community-extensions/files становится /u/p/m/b/files
  • /home/user1/media/video/music/live-sets становится ~/m/v/m/live-sets (обратите внимание на символ ~ в качестве замены для $HOME)
  • /home/user2/media НЕ изменяется (ограничение 20 char не превышено)
  • /home/user1/this-is-a-very-long-path-name-with-more-than-20-chars становится ~/this-is-a-very-long-path-name-with-more-than-20-chars (последний компонент остается без искажений: мягкий предел)
  • /home/user1/src становится ~/src ($ HOME всегда сокращается)
  • /home/user1/.kde4/share/config/kresources становится ~/.k/s/c/kresources (обратите внимание, что точка префикса сохраняется)

Текущий пользователь - user1.

Это позволило вызвать внешние интерпретаторы, такие как awk, perl, ruby, python, но не скомпилированные C-программы или аналогичные. Другими словами: внешние исходные файлы недопустимы, код должен быть встроенным. Побеждает самая короткая версия. Длина тела функции bash (и называется подфункциями) подсчитывается, означает:

_sub_helper() {
  # this counts
}
_dir_chomp() {
  # these characters count (between { and })
  _sub_helper "foobar" # _sub_helper body counts, too
}
4b9b3361

Ответ 1

Pure Bash:

_dir_chomp () {
    local IFS=/ c=1 n d
    local p=(${1/#$HOME/\~}) r=${p[*]}
    local s=${#r}
    while ((s>$2&&c<${#p[*]}-1))
    do
        d=${p[c]}
        n=1;[[ $d = .* ]]&&n=2
        ((s-=${#d}-n))
        p[c++]=${d:0:n}
    done
    echo "${p[*]}"
}

В целях тестирования я предполагаю, что вопрос означает, что текущий пользователь "user1".

Примечание: Bash 4 имеет переменную PROMPT_DIRTRIM, которая сокращает \w escape в PS1, сохраняя количество подкаталогов в соответствии со своим значением и заменяя остальные на ...

/$ PROMPT_DIRTRIM=2
/$ echo $PS1
\w\$
/$ pwd
/
/$ cd /usr/share/doc/bash
.../doc/bash$

Ответ 2

Это примерно 20 символов, чем мой другой ответ:

_dir_chomp () {
    local p=${1/#$HOME/\~} b s
    s=${#p}
    while [[ $p != "${p//\/}" ]]&&(($s>$2))
    do
        p=${p#/}
        [[ $p =~ \.?. ]]
        b=$b/${BASH_REMATCH[0]}
        p=${p#*/}
        ((s=${#b}+${#p}))
    done
    echo ${b/\/~/\~}${b+/}$p
}

Ответ 3

Это было мое собственное решение, когда у меня возникла идея этой проблемы. Вдохновение действительно произошло от Jolexa Blog.

Итак, вот, реализация ruby ​​в читаемой форме:

a = ARGV[1].gsub(%r{^#{ENV['HOME']}}, "~")
b, a = a, a.gsub(%r{/(\.?[^/.])[^/]+(/.*)}, '/\1\2') while
  (a.length > ARGV[2].to_i) && (b != a)
print a

И фактическая однострочная реализация в функции bash:

_dir_chomp() {
  ruby -e'a="'$1'".gsub(%r{^'$HOME'},"~");b,a=a,a.gsub(%r{/(\.?[^/.])[^/]+(/.*)},"/\\1\\2")while(a.length>'$2')&&(b!=a);print a'
}

Ответ 4

Другое решение с использованием только внутренних компонентов bash, без использования sed

shortpath()                                                                                                                                                                                                                                                                   
{           
    dir=${1%/*} && last=${1##*/}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                

    res=$(for i in ${dir//\// } ; do echo -n "${i:0:3}../" ; done)                                                                                                                                                                                                            
    echo "/$res$last"                                                                                                                                                                                                                                                         
} 

Мое предыдущее решение, с bash и sed. он разрезал каждый каталог на 3 первых символа и добавил '..' вот так: /hom../obo../tmp../exa../bas../

shortpath()
{
        dir=$(dirname $1)
        last=$(basename $1)

        v=${dir//\// } # replace / by <space> in path
        t=$(printf "echo %s | sed -e 's/^\(...\).*$/\\\1../' ; " $v) 
            # prepare command line, cut names to 3 char and add '..'
        a=$(eval $t) 
            # a will contain list of 3 char words ended with '..' ans separated with ' '

        echo " "$a"/"$last | sed -e 's/ /\//g'
}

Ответ 5

Вот как я сокращаю свой bash запрос w/полный путь в заголовке (работает с 3.0):

_PS1P=('' '..')
PROMPT_COMMAND='_PS1L=${#DIRSTACK[0]} _PS1D=${DIRSTACK[0]}'
PS1='\[\e]2;\h:\w\a\]\h ${_PS1P[$_PS1L>36]}${_PS1D:$_PS1L>36?-34:0} \$ '

Этот метод требует очень низких затрат на процессор.