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

发布于 2024-09-15 03:10:55 字数 1414 浏览 9 评论 0原文

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

当前用户是 user1。

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

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

I implemented a prompt path shortener for bash to be included in the PS1 environment variable, which shortens the working directory into something more compact but still descriptive. I'm curious what other ideas may exist.

Here's the challenge:

Create a bash function _dir_chomp which can be included into PS1 like this (line breaks inserted for readability):

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

with "20" being the parameter for the maximum length as soft limit. These are the examples:

  1. /usr/portage/media-plugins/banshee-community-extensions/files becomes /u/p/m/b/files
  2. /home/user1/media/video/music/live-sets becomes ~/m/v/m/live-sets (note the ~ character as replacement for $HOME)
  3. /home/user2/media does NOT change (20 char limit not exceeded)
  4. /home/user1/this-is-a-very-long-path-name-with-more-than-20-chars becomes ~/this-is-a-very-long-path-name-with-more-than-20-chars (last component stays unshortened: soft limit)
  5. /home/user1/src becomes ~/src ($HOME always shortened)
  6. /home/user1/.kde4/share/config/kresources becomes ~/.k/s/c/kresources (note the prefixing dot is preserved)

Current user is user1.

It's allowed to call external interpreters like awk, perl, ruby, python but not compiled C programs or similar. In other words: external source files are not allowed, code must be inline. Shortest version wins. The length of the bash function body (and called sub functions) counts, means:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

熟人话多 2024-09-22 03:10:55

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,它通过根据其值保留子目录的数量来缩短 PS1 中的 \w 转义符将其余部分替换为 ...

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

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[*]}"
}

For purposes of testing, I'm assuming that the question means that the current user is "user1".

Note: Bash 4 has a variable PROMPT_DIRTRIM that shortens the \w escape in PS1 by retaining the number of sub-directories according to its value and replacing the rest with ...

/$ PROMPT_DIRTRIM=2
/$ echo $PS1
\w\$
/$ pwd
/
/$ cd /usr/share/doc/bash
.../doc/bash$
深海少女心 2024-09-22 03:10:55

这个比我的另一个答案短 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
}

This one is 20 or so characters shorter than my other answer:

_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
}
满地尘埃落定 2024-09-22 03:10:55

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

This was my own solution when I had the idea for this challenge. The inspiration actually came from Jolexa's Blog.

So here it is, the ruby implementation in readable form:

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

And the actual one-line implementation within the bash function:

_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'
}
相对绾红妆 2024-09-22 03:10:55

另一个只有 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'
}

Another solution with only bash internals, no use of sed

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

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

My previous solution, with bash and sed.
it cut each dir in 3 first caracters and add '..' like this:
/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'
}
最近可好 2024-09-22 03:10:55

这就是我如何缩短 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 开销。

This is how I shorten my bash prompt w/ full path in titlebar (works since 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} \$ '

This method requires very low CPU overhead.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文