21

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

这是挑战:

创建一个_dir_chomp可以像这样包含到 PS1 中的 bash 函数(插入换行符以提高可读性):

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(注意前缀点被保留)

当前用户是user1。

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

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

5 回答 5

13

纯重击:

_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通过根据其值保留子目录的数量并将其余的替换为\wPS1...

/$ PROMPT_DIRTRIM=2
/$ echo $PS1
\w\$
/$ pwd
/
/$ cd /usr/share/doc/bash
.../doc/bash$
于 2010-08-17T02:30:33.937 回答
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
}
于 2010-08-17T04:02:06.930 回答
1

当我有这个挑战的想法时,这是我自己的解决方案。灵感实际上来自Jolexa 的博客

所以这里是可读形式的 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'
}
于 2010-08-17T07:46:40.257 回答
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'
}
于 2019-09-16T07:57:20.977 回答
-1

这就是我如何在标题栏中缩短带有完整路径的 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 开销。

于 2014-09-20T16:16:49.083 回答