代码挑战:Bash提示路径缩短

时间:2010-08-16 22:20:46

标签: bash path prompt

我实现了一个提示路径缩短器,用于将bash包含在PS1环境变量中,这会将工作目录缩短为更紧凑但仍具有描述性的内容。我很好奇其他想法可能存在。

这是挑战:

创建一个bash函数_dir_chomp,它可以像这样包含在PS1中(插入换行符以便于阅读):

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

以“20”为最大长度的参数作为软限制。以下是这些例子:

  1. /usr/portage/media-plugins/banshee-community-extensions/files变为/u/p/m/b/files
  2. /home/user1/media/video/music/live-sets变为~/m/v/m/live-sets(请注意〜字符作为$ HOME的替代品)
  3. /home/user2/media不会更改(不超过20个字符限制)
  4. /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(最后一个组件保持未被缩短:软限制)
  5. /home/user1/src变为~/src($ HOME总是缩短)
  6. /home/user1/.kde4/share/config/kresources变为~/.k/s/c/kresources(请注意保留前缀点)
  7. 当前用户是user1。

    允许调用外部解释器,如awkperlrubypython,但不能编译C程序或类似程序。换句话说:不允许使用外部源文件,代码必须是内联的。最短版本获胜。 bash函数体的长度(和称为子函数)计数,意味着:

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

5 个答案:

答案 0 :(得分:12)

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,通过根据其值保留子目录的数量并用{{1替换其余的目录来缩短\w中的PS1转义。 }}

...

答案 1 :(得分:8)

这个字符比我的其他答案短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
}

答案 2 :(得分:1)

当我有了这个挑战的想法时,这是我自己的解决方案。灵感实际上来自Jolexa's 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'
}

答案 3 :(得分:0)

仅有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'
}

答案 4 :(得分:-1)

这是我用标题栏中的/ full路径缩短我的bash提示符(从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} \$ '

此方法需要非常低的CPU开销。