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

11

我正在寻找一种实现响应式工作目录的方法,至少要显示第一个和最后一个文件夹,并且应基于终端中可用的宽度。例如,类似于$(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脚本实现此功能?


我认为这是可能的。您可以将PS1设置为export PS1="\u@\h ~/somedir/koba.sh > "。然后创建此脚本(koba.sh)或任何您喜欢的名称。在该脚本中,使用pwd并只使用一个“echo”语句来回显您想要的格式/workspace/../xx/zz"或者计算您想要作为输出回显的内容(它将考虑会话窗口的环境变量COLUMNS的值),即每次使用鼠标最小化/最大化屏幕大小时都应更改PS1。需要注意的一个重要点是,如果您在/workspace文件夹中,则您的PS1不应回显~/../workspace。 - AKS
我为您发布了一个基础脚本供您查看。如果您稍微改进一下,就可以得到您需要的东西。我在我的机器上测试过它,只要我最小化/最大化屏幕,它就能正常工作,尽管我不得不重新导出PS1(可能有一种自动设置PS1的方法)。 - AKS
我记得几年前想要这样的东西。但后来我决定在我的提示符之前,在“$”之前放一个\n。解放啊! :) - PM 2Ring
2个回答

20

最简单情况的解决方案

我想要显示类似于这样的提示:~/workspace/../src

这将以通常的$和空格结束提示,同时显示该路径样式:

PS1='$(pwd | sed -E -e "s|^$HOME|~|" -e '\''s|^([^/]*/[^/]*/).*(/[^/]*)|\1..\2|'\'') \$ '
尽管我的意图是针对OSX的BSD版sed进行这项工作,但我只在使用GNU版sed的Linux上进行了测试。
此版本仅提供一种输出格式。 它不会随着终端变宽而更改格式。

它是如何工作的

定义新的PS1中的关键元素是命令替换。该命令为:
pwd | sed -E -e "s|^$HOME|~|" -e 's|^([^/]*/[^/]*/).*(/[^/]*)|\1..\2|'
  • 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;}'\'') \$ '

由于这种解决方案需要更多的计算,所以使用了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;}'
代码考虑以下情况:
  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;} print b e;

    This code shortens the directory string if it exceeds a certain length, by removing directories and replacing them with /../. The shortened string is then printed as the final directory string in the command prompt. To change the width of the display, the command n=0.3*n should be replaced with 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;}'\'') \$ '
    

    根据您的个人偏好,您可能需要其他公式。例如,尝试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\$ '
    

谢谢。虽然它没有解决主要请求。请澄清一下。我的窗口(COLUMNS> 200)和您的解决方案打印了/topfolder/lastfolder(而不是/topfolder/full/path/to/lastfolder或/topfolder/../lastfolder)。我认为这是他请求的重点,如果宽度/COLUMNS足够适合PWD路径,则应在PS1中打印PWD的完整值,如果COLUMNS/窗口大小小于完整PWD中的字符,则将使用/topfolder/../lastfolder(考虑到一个例外情况,即如果您实际上在/short/dir文件夹中,则会将PS1回显为/short/dir而不是~/short/../dir)。 - AKS
1
@MarcelOverdijk 是的,有一个调整选项。请查看更新后的答案。我更喜欢一个简短的提示,所以我最初设置了目录字符串占据宽度的30%的目标。我添加了一个示例,其中目录字符串可以占用整个宽度,并提供了如何进行实验以获得您想要的精确信息。在我的原始代码中,30%的目标是由行n=0.3n设置的。如更新后的答案所示,您可以根据需要更改该公式。 - John1024
1
嗯,不确定为什么它对我起作用方式不同,但我不得不将n>length($i)更改为n>length(e)。看起来这是一个笔误,因为你想要字符串的长度小于n,而不是路径的索引... - Glen Takahashi
@GlenTakahashi 在看到你的评论之前,我也得出了同样的结论。这对我也有效。我建议将 s/length($i)/length(e) 作为解决方案的编辑建议。 - mgiuffrida
非常好!@mgiuffrida和Glen Takahashi,我根据你们的建议更新了答案。 - John1024
显示剩余8条评论

1

我创建了一个基础脚本(仍需要一些增强来执行最后的计算路径,以找出在“〜/工作区/.. /”之后显示多少lpart(最后部分或后缀部分)(fpart或前缀),否则,如果您:



1. 导出COLUMNS

2. 将您的PS1设置为“*export PS1 =” `〜/ somedir / koba.sh` $“

当您的COLUMNS值小于echo 〜/ workspace | wc -c 时,我已经测试了~/workspace文件夹的条件。这个基本示例脚本将仅将lpart /后缀/最后一个文件夹作为最后一个文件夹(因为我使用basename命令)。唯一的其他事情是,我看到每次都必须导出PS1(在最小化/最大化窗口后,但之后它按预期工作)。


[giga@someserver somePS1]$ cat ~/somedir/koba.sh

#!/bin/bash

## NOTE this script should echo only 1 echo output and exit out as soon as you echo 
## your first echo.

cdir=`pwd`
wc_dir=$(echo $cdir | wc -c)
bal=$(expr $COLUMNS - $wc_dir)

if [[ "`echo $(pwd) | tr '/' '\012'| wc -l`" -le 2 || $bal -ge $wc_dir ]];
then
 echo "`pwd`"; exit 0 ;
else
 fpart="$(echo $cdir | cut -d'/' -f1,2)/../"
 lpart="$(basename $cdir)"
 ## Calculate lpart (last part) in an better way to show ../aaa or ../aaa/bbb or ../aaa/bbb/c1 or more depending upon $COLUMNS and $wc_dir
 ## Do that math here.
 ## ...
 ## ...

 echo "${fpart}${lpart}"; exit 0;
fi

或者

导出这两个环境变量后,如果您:

只需将以下环境变量设置为脚本。每次在显示PS1之前调用此变量以运行脚本。因此,它将设置dir值,并将PS1设置为以给定格式显示。使用此功能,现在您不需要在更改窗口/屏幕大小或移动到不同父/子文件夹的路径时每次重置PS1。只需按Enter键(最小化/最大化窗口),然后查看,它就起作用了!

export PROMPT_COMMAND="~/somedir/koba.sh"

我们需要找到一种方法:如何在运行$提示符下的任何命令后自动“重置”PS1,或者当有人更改窗口大小时(也许编辑器本身存在某些设置来更改PS1的大小)。 - AKS
好的,太棒了。我找到了最终答案。好消息,这是可能的。 - AKS
导出 PROMPT_COMMAND="~/somedir/koba.sh" ... 就是这样。现在,最小化或最大化您的屏幕,并在 $ 提示符处按回车键(无需重置 PS1)...它就像魔法一样运行。 - AKS
这个请求中只有一个缺陷(不在我的解决方案中),如果叶子文件夹的名称是“sooooooooooooooooooooooooooooooooooooooooooLoooooooooooooooooooooooNNNNNNNNNNNNNNGGGGGGGGGGGGGGGGGGtttttttttooooooooPPPPPPPPPPPPPRINT”,那么我怀疑我们能否完成这个任务。有什么想法吗?任何人。 - AKS

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