如何在提示中截断工作目录以显示第一个和最后一个文件夹?

Mar*_*ijk 8 macos bash prompt

我正在寻找一种方法来实现一个响应式工作目录,我至少想要显示第一个和最后一个文件夹.它应该基于终端中的可用.例如$(exprtput cols之类的东西- 35)}.

想象一下当前的工作目录是什么 ~/workspace/i/keep/my/projects/project1/src

然后我想显示提示,如:

~/workspace/../src

如果终端足够大我想拥有:

~/workspace/../project1/src

要么

~/workspace/../projects/project1/src

~/workspace/../my/projects/project1/src

所以前进.

如果有足够的地方,它甚至应该显示完整的路径.为了赢得空间,家庭目录应始终显示为~.

这是否可以在OSX上使用纯bash脚本?

Joh*_*024 13

最简单案例的解决方案

我想显示提示,如: ~/workspace/../src

这将在使用通常$和空格结束提示时显示该路径样式:

PS1='$(pwd | sed -E -e "s|^$HOME|~|" -e '\''s|^([^/]*/[^/]*/).*(/[^/]*)|\1..\2|'\'') \$ '
Run Code Online (Sandbox Code Playgroud)

虽然我的目的是在OSX的BSD上进行这项工作sed,但我只在Linux上使用GNU进行了测试sed.

此版本仅提供一种输出格式.随着终端变宽,它不会改变格式.

这个怎么运作

新定义中的关键元素PS1是命令替换.那个命令是:

pwd | sed -E -e "s|^$HOME|~|" -e 's|^([^/]*/[^/]*/).*(/[^/]*)|\1..\2|'
Run Code Online (Sandbox Code Playgroud)
  • pwd

    这会将当前工作目录打印到stdout

  • "s|^$HOME|~|"

    如果路径开头$HOME,请将其替换为~.因为$HOME是shell变量,所以此命令必须是双引号,以便shell执行变量替换$HOME.

  • 's|^([^/]*/[^/]*/).*(/[^/]*)|\1..\2|'

    正则表达式由三部分组成:

    • ^([^/]*/[^/]*/)匹配前两个目录并将其保存\1.

    • .* 这匹配前两个目录之前的任何内容,直到最后一个目录

    • (/[^/]*)匹配最后一个子目录并将其保存\2.

    如果有足够的目录供正则表达式匹配,那么整个路径将替换为\1..\2.

更复杂的解决方案

假设我们希望提示的格式随终端的可用宽度而改变,如果有空间则显示更多的最终目录.因此,在一个狭窄的终端中,/home/user/dir1/dir2/dir3/dir4可能会显示为,~/dir1/../dir4但是在更宽的终端中它将显示为, ~/dir1/../dir3/dir4并且在更宽的终端中,它将显示为~/dir1/dir2/dir3/dir4.

在这种情况下:

PS1='$(pwd|awk -F/ -v "n=$(tput cols)" -v "h=^$HOME" '\''{sub(h,"~");n=0.3*n;b=$1"/"$2} length($0)<=n || NF==3 {print;next;} NF>3{b=b"/../"; e=$NF; n-=length(b $NF); for (i=NF-1;i>3 && n>length(e)+1;i--) e=$i"/"e;} {print b e;}'\'') \$ '
Run Code Online (Sandbox Code Playgroud)

因为此解决方案需要更多计算,awk所以使用.

此代码允许用户调整目录列表应占用的终端宽度.这是通过调整代码中的常量来完成的n=0.3*n.如上所述,如果可能,这会将目录显示限制为仅30%的终端宽度.

这个怎么运作

代码的关键元素是这个命令:

pwd | awk -F/ -v "n=$(tput cols)" -v "h=^$HOME" '{sub(h,"~");n=0.3*n;b=$1"/"$2} length($0)<=n || NF==3 {print;next;} NF>3{b=b"/../"; e=$NF; n-=length(b $NF); for (i=NF-1;i>3 && n>length(e)+1;i--) e=$i"/"e;} {print b e;}'
Run Code Online (Sandbox Code Playgroud)

代码考虑了这些情况:

  1. 目录字符串已经足够短了.换句话说,它的长度适合分配的空间.在这种情况下,它按原样显示.

  2. 只有三个目录.例如,~/dir1/dir2.我们的格式不允许缩短.因此,对于这种情况,目录字符串按原样显示.

  3. 有四个或更多目录,必须缩短目录字符串以适应空间.在这种情况下,目录字符串被分为存储在变量中的开头和存储在变量中b的结尾e.开头包含两个目录和点 - 点字符串,例如~/dir1/../.从最后一个目录开始,e当空间允许时,目录将添加到结束字符串.

有关awk命令的更多详细信息

  • -F/

    这会将字段分隔符设置为/.因此,每个目录将显示为单独的字段.

  • -v "n=$(tput cols)" -v "h=^$HOME"

    这会创建我们需要的两个变量. n具有列中的终端宽度. h有一个正则表达式匹配用户的主目录.

  • sub(h,"~")

    如果路径以用户的主目录开头,则将其替换为~.

  • n=0.3*n

    这为提示中的目录字符串的所需宽度设置了目标.我喜欢目录字符串不太长,因此n=0.3*n将目标设置为列中终端宽度的30%.如其他地方所述,根据您的个人喜好,可以用另一个公式替换.

  • b=$1"/"$2

    b是包含目录字符串开头的变量.在这里,我们将它设置为前两个目录.例如,如果路径是~/dir1/dir2/dir3,则设置b~/dir1.

  • length($0)<=n || NF==3 {print;next;}

    如果完整的目录字符串不长于我们的目标,n或者如果目录字符串中只有三个目录,则按原样打印出目录字符串,然后退出.

  • NF>3{b=b"/../"; e=$NF; n-=length(b $NF); for (i=NF-1;i>3 && n>length(e)+1;i--) e=$i"/"e;}

    如果我们得到,那么我们的目录字符串需要缩短.因此,我们将缩写字符串添加/../到末尾b.对于第一次试验,我们将结束字符串设置e为最后一个目录.(在awk,NF是字段的数量.因此,$NF是最后一个字段(目录)就行了.)然后,我们从减去n的长度be.n剩下的价值是我们剩下的空间量.然后从该行的倒数第二个目录开始,我们尝试一次添加一个目录,e而不会超过我们的目标,即目录字符串应该有多长.

  • {print b e;}

    这将打印提示中使用的最终目录字符串.

全宽显示

要更改目录显示所占用的空间量,我们将读取的命令n=0.3*n(目标为30%宽度)更改为n=1*n(即使$提示符溢出到下一行,也会以全宽度为目标):

PS1='$(pwd|awk -F/ -v "n=$(tput cols)" -v "h=^$HOME" '\''{sub(h,"~");n=1*n;b=$1"/"$2} length($0)<=n || NF==3 {print;next;} NF>3{b=b"/../"; e=$NF; n-=length(b $NF); for (i=NF-1;i>3 && n>length(e)+1;i--) e=$i"/"e;} {print b e;}'\'') \$ '
Run Code Online (Sandbox Code Playgroud)

根据您的喜好,您可能需要其他公式.例如,尝试n=n-10并尝试在提示结束时为您留下一些(但不是很多)可用空间.

PM2Ring建议将$提示始终放在下一行,为此,我们\n\$提示之前放置一个(换行符):

PS1='$(pwd|awk -F/ -v "n=$(tput cols)" -v "h=^$HOME" '\''{sub(h,"~");n=1*n;b=$1"/"$2} length($0)<=n || NF==3 {print;next;} NF>3{b=b"/../"; e=$NF; n-=length(b $NF); for (i=NF-1;i>3 && n>length(e)+1;i--) e=$i"/"e;} {print b e;}'\'') \n\$ '
Run Code Online (Sandbox Code Playgroud)