【问题标题】:How to truncate working directory in prompt to show first and last folder?如何在提示中截断工作目录以显示第一个和最后一个文件夹?
【发布时间】:2014-12-20 16:37:24
【问题描述】:

我正在寻找一种方法来实现响应式工作目录,我至少想在其中显示第一个和最后一个文件夹。 它应该基于终端中的可用。例如。类似$(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
  • 我发布了一个基本脚本供您查看。如果你稍微增强它,你会得到你需要的。我在我的机器上对其进行了测试,一旦我最小化/最大化屏幕,它就可以工作,但是,我不得不重新导出 PS1(可能有一种方法可以自动设置 PS1)
  • 我记得几年前就想要这样的东西。但后来我决定在提示符的末尾加上\n,就在\$ 之前。解放! :)

标签: macos bash prompt


【解决方案1】:

最简单情况的解决方案

我想显示如下提示:~/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

    这会将当前工作目录打印到标准输出

  • "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)&lt;=n || NF==3 {print;next;}

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

  • NF&gt;3{b=b"/../"; e=$NF; n-=length(b $NF); for (i=NF-1;i&gt;3 &amp;&amp; n&gt;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;}'\'') \$ '

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

【讨论】:

  • 谢谢约翰。在 Linux 机器上,设置此 PS1 时出现以下错误。它说: sed: 无效选项 -- E
  • @ArunSangal 在这种情况下,使用sed -r 代替sed -E。在我的 GNU sed (v4.2.1) 上,两者做同样的事情,-E 是与 BSD sed 兼容的格式。
  • 谢谢。虽然它没有解决主要请求。请澄清。我的窗口 (COLUMNS > 200) 和您的解决方案打印了 ~/topfolder/lastfolder (比 ~/topfolder/full/path/to/lastfolder 或 ~/topfolder/../lastfolder) 我认为这是他要求的重点,如果width / COLUMNS 足以适合 PWD 路径,然后它应该在 PS1 中打印 PWD 的完整值,如果 COLUMNS/windows 大小小于完整 PWD 中的字符,那么它将使用 ~/topfolder/../lastfolder (考虑到一个例外情况,如果你实际上在 ~/short/dir 文件夹中,那么它会将 PS1 回显为 ~/short/dir 而不是 ~/short/../dir)
  • @MarcelOverdijk 是的,对此进行了调整。请参阅更新的答案。我更喜欢简短的提示,所以我最初将目录字符串的目标设置为占宽度的 30%。我添加了一个示例,其中允许目录字符串占据整个宽度以及有关如何进行实验以准确获得所需内容的信息。在我的原始代码中,30% 的目标是由 n=0.3n 行设置的。如更新后的答案所示,您可以根据需要更改该公式。
  • 嗯,不知道为什么它对我的工作方式不同,但我不得不将 n>length($i) 更改为 n>length(e)。这似乎是一个错字,因为您希望字符串的长度小于 n,而不是路径的索引...
【解决方案2】:

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

1. 导出 COLUMNS

2. 将您的 PS1 设置为"*export PS1="`~/somedir/koba.sh`$"

当您的 COLUMNS 值小于 echo ~/workspace | wc -c 时,我也测试了 ~/workspace 文件夹的条件。这个基本示例脚本只会将 lpart/suffix/last 部分作为最后一个文件夹(因为我正在使用 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

导出这 2 个 ENV 变量后。如果你:

只需将以下 ENVIRONMENT 变量设置为脚本。每次显示 PS1 之前,此变量都会调用脚本。因此,它将设置 dir 值并将 PS1 设置为以给定格式显示。使用此功能,您现在不必在每次更改窗口/屏幕尺寸或移动到不同父/子文件夹的路径时都必须重置 PS1。只需按回车键(最小化/最大化窗口),看看,它的工作原理!!!

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

【讨论】:

  • 我们需要找到一种方法:如何在 $ 提示符下运行任何命令后自动“重置”PS1,就像我上面提到的那样,或者一旦有人更改了窗口大小(可能是一些设置在编辑器本身中存在 PS1 的这种大小变化)。
  • 好的,太好了。我找到了最终答案。好消息。这是可能的。
  • export PROMPT_COMMAND="~/somedir/koba.sh" ... 这样就可以了。现在,最小化或最大化您的屏幕,只需在 $ 提示符下按 Enter 键(无需重置 PS1 和)...它就像一个魅力。
  • 整个请求中只有一个缺陷(不在我的解决方案中),如果叶子文件夹是“sooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooNNNNNNNNNNNNNNNGGGGGGGGGGGGGGGGGtttttttttttoooooooPPPPPPPPPPPPPRINT”,那么我怀疑我们能否完成此任务。任何想法!!!任何人。
猜你喜欢
  • 2021-03-06
  • 1970-01-01
  • 1970-01-01
  • 2018-12-12
  • 2019-06-12
  • 1970-01-01
  • 2019-03-12
  • 1970-01-01
  • 2021-02-11
相关资源
最近更新 更多