Bash提示符缩短

5

我刚从tcsh换成了bash, 特别是在使用%c02(带有设置的省略号)时,我非常想念缩短目录提示选项。

我看到PROMPT_DIRTRIM几乎做到了正确的事情(除了省略号),但我只用bash 3(在OS X上)。我在SO上找到了这个配方,它根据总长度缩短,所以会在目录中间断开路径名称,这不符合我的需求。

因此,我想出了以下方法:

PROMPT_DIRTRIM=2   ## from bash4, but used here
dirtrim() 
{
    local NAME="$1" start= endelts=
    [[ "$NAME" =~ ^"$HOME"(/|$) ]] && NAME="~${NAME#$HOME}"  ## $HOME ==> ~
    IFS=/ read -ra elts <<< "$NAME";          ## split $PWD on "/"
    start=$((${#elts[@]}-${PROMPT_DIRTRIM}))  ## first element to retain
    if [ ${start} -gt 1 ]; then     
        for ((i=${start}; i<${#elts[@]}; i++)); do 
            endelts="${endelts}/${elts[$i]}"; ## concat together the trailing path
        done
        NAME="...${endelts}"
    fi
    echo "$NAME"    
}
PS1='\h:$(dirtrim "$PWD")\$ '

它能工作:

blackat:~$ cd ~/Library/Application\ Support/Apple
blackat:.../Application Support/Apple$

但我对bash还不是很熟悉,也不喜欢显式的for(())循环;但是,我似乎找不到其他方法来以正确处理目录名称中的空格(例如使用${elts[@]:${start}})重新连接拆分的elts数组的最后条目。有什么提示可以做到这一点或者其他改进吗?
(顺便说一下,我认为这是一个编程问题,因为bash是一种编程语言....)

1
Bash是一种编程语言。 - Jonathan Leffler
@Jonathan Leffler:同意——这是对两个关闭投票的回应! - Andrew Jaffe
2
我确认关于Bash编程的问题是编程问题,因此在Stack Overflow上属于适当的话题。关闭有关shell编程的问题作为不适当的话题曾经是一个主要问题;现在,我没有看到这种情况发生得很多,但这可能只意味着我没有注意到,并且它一直在发生。顺便说一下:我也不喜欢长提示:我根本不在提示中包含路径。在一个名为“sphinx”的机器上,我的提示是Sphinx JL:。在Mac终端中,您可以在终端标题栏(属性->窗口并查找标题部分)中包含路径。 - Jonathan Leffler
2个回答

3
您可以尝试以下方法:
if ((start > 1)); then
  name=$(IFS=/; echo .../"${elts[*]:start}")
  # If your terminal is correctly set up for unicode, you can save two character positions:
  # name=$(IFS=/; echo …/"${elts[*]:start}")
fi

请注意,在bash中,算术上下文包括((...))和数组下标,在其中,您只需编写变量名称即可,无需标记。
另一种方法是:
if ((start > 1)); then
  printf -v name "/%s" "${elts[@]:start}"
  name=...$name
fi

另一种解决方案是使用正则表达式捕获在BASH_REMATCH数组中,而不是分割和重新组合字符串:

dirtrim () { 
  local path="$1";
  [[ $path =~ ^"$HOME"(/.*)? ]] && path=~${BASH_REMATCH[1]};
  ((PROMPT_DIRTRIM)) &&
    [[ $path =~ ...*((/[^/]*){$PROMPT_DIRTRIM}) ]] &&
    path=…${BASH_REMATCH[1]};
  echo "$path"
}

((PROMPT_DIRTRIM))测试并不完全健壮,因为在算术上下文中bash的评估非常特殊。您可能更喜欢分配类似于[[ $PROMPT_DIRTRIM =~ ^[1-9][0-9]*$ ]]的内容。


  1. 关于大小写问题达成一致;我从另一个答案中得到了NAMES
  2. 我的意思是((start>1))--如果start==1,则只保留第一个条目;
  3. 我认为的意思是name=$(IFS=/; echo .../"${elts[*]:start}")(注意:IFS=/和缺少$)。
- Andrew Jaffe
@andrew:Bash数组的第一个条目索引为0。你关于错别字的说法是正确的,当我打字时我正在匆忙离开门口。抱歉,我现在已经修复了它。 - rici
是的,但第零个条目是“〜”或null,因此我们只想在start>1时缩短。 - Andrew Jaffe
@AndrewJaffe:啊,说得好。如果不需要缩短,就没有缩短的理由。为了您的观看愉悦,我添加了一个使用正则表达式的解决方案,完全避免了分割和连接。 - rici

1

这并不是一个答案,但你可能想看看 mksh 是如何实现这个功能的:

PS1=${| local e=$? (( e )) && REPLY+="$e|" REPLY+=${USER:=$(ulimit -c 0; id -un 2>/dev/null || echo \?)} REPLY+=@${HOSTNAME%%.*}: local d=${PWD:-?} p=~; [[ $p = ?(*/) ]] || d=${d/#$p/~} local m=${%d} n p=...; (( m > 0 )) || m=${#d} (( m > (n = (COLUMNS/3 < 7 ? 7 : COLUMNS/3)) )) && d=${d:(-n)} || p= REPLY+=$p$d return $e }

不幸的是,它使用了一些在中不存在的扩展。

另外,既然你要切换shell……你考虑过使用fish吗?

它可以直接实现这个功能,而且还有更多其他功能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接