如何缩短命令行提示符中的当前目录?

62

我正在使用Ubuntu,不想在处理深目录层级时看到命令提示符过长。因此,我想要调整我的PS1,以便将工作目录的长度缩短为以下样式:

目前我的提示符是:

pajton@dragon:~/workspace/projects/project1/folder1/test$

我希望拥有:

pajton@dragon:~/workspace/.../folder1/test$

如果 $PWD 的长度超过给定的阈值,则会进行截断。我希望始终保留第一个路径组件和至少一个最后路径组件。然后,随着空间的允许,从右侧添加更多组件。

这是我目前的代码。它能工作,但存在以下问题:1)不保留第一个路径组件;2)不在边界处截取路径:

pwd_length=14
pwd_symbol="..."
newPWD="${PWD/#$HOME/~}"

if [ $(echo -n $newPWD | wc -c | tr -d " ") -gt $pwd_length ]
then
   newPWD="...$(echo -n $PWD | sed -e "s/.*\(.\{$pwd_length\}\)/\1/")"
else
   newPWD="$(echo -n $PWD)"
fi

结果如下:

pajton@dragon:...sth/folder1/sample$ 

提前致谢!


3
你可以选择将工作目录和实际提示符放在不同的行上,使用换行符 (\n) 在 PS1 中实现。 - ak2
3
另一种选择是在PS1结尾处加入换行符,这样你就可以看到整个路径,但仍然可以输入长命令而不会自动换行。无论如何,这就是我所做的 :) - Mark Longair
抱歉,我点击“添加评论”时没有看到你的评论。 - Mark Longair
那也是一个解决方案,但个人不喜欢两行提示。太啰嗦了 :) - pajton
1
非常好的问题,点赞!我也不喜欢两行提示符,并且曾经遇到过命令在深层嵌套目录中换行的情况。但是现在,我已经按照下面的答案设置了我的环境,感觉好多了 :). - anubhava
顺便提一下,您可以使用省略号字符 而不是三个空格 ... 来节省几个字符。 - Sparhawk
12个回答

132

对于那些寻求更简单解决方案且不需要路径中第一个目录名称的人来说,Bash内置了对此的支持,使用PROMPT_DIRTRIM变量即可。来自文档:

PROMPT_DIRTRIM

如果设置为大于零的数字,则该值用作扩展\ w和\ W提示字符串转义时要保留的尾部目录组件的数量(请参见打印提示)。删除的字符将被省略号替换。

例如:

~$ mkdir -p a/b/c/d/e/f
~$ cd a/b/c/d/e/f
~/a/b/c/d/e/f$ export PROMPT_DIRTRIM=2
~/.../e/f$ PROMPT_DIRTRIM=3
~/.../d/e/f$ 

缺点:它取决于目录级别,而不是路径长度,这可能不是你想要的。

优点:非常简单。只需在您的.bashrc中添加export PROMPT_DIRTRIM=2即可。


9
嗯嗯...在我的OSX电脑上这个方法不起作用。我把它放到了我的.bash_profile文件中(我想在OSX上这是适当的...在新的终端窗口中肯定会执行),并尝试从命令提示符中运行“export PROMPT_DIRTRIM=2”。 - ken
3
在苹果公司预装的bash版本(我使用的是Mountain Lion)中无法运行。预装版本是3.2.48(1)-release。如果你使用Homebrew等方式安装最新的bash,你会得到一个接近4.2.45(2)-release的版本,它支持PROMPT_DIRTRIM。请注意,这里提到的是OSX操作系统。 - Joey Gibson
1
是的,这个环境变量是在Bash 4版本中引入的。 - Jack
5
哇,2016年的 OS X 仍然附带有 Bash 3.2.57,版权所有 (c) 2007。 - LarsH
2
@LarsH 因为bash 3.2是最后一个以GPLv2+发布的版本。Bash 4.0+是GPLv3+,而苹果公司有严格的政策不允许发布GPLv3软件。 - imgx64
我想要能够点击这个10次。 - andypotter

49

考虑使用awk而不是sed来处理您的情况:

pwd_length=14
pwd_symbol="..."
newPWD="${PWD/#$HOME/~}"
if [ $(echo -n $newPWD | wc -c | tr -d " ") -gt $pwd_length ]
then
   newPWD=$(echo -n $newPWD | awk -F '/' '{
   print $1 "/" $2 "/.../" $(NF-1) "/" $(NF)}')
fi
PS1='${newPWD}$ '

针对您的目录示例~/workspace/projects/project1/folder1/test,该命令会将PS1设置为:~/workspace/.../folder1/test

更新

上述解决方案将设置您的提示符,但如您在评论中所提到的,它不会在您更改目录时动态更改PS1。因此,以下是当您在不同目录下更改时会自动设置PS1的解决方案。

将以下两行代码放入您的.bashrc文件中:

export MYPS='$(echo -n "${PWD/#$HOME/~}" | awk -F "/" '"'"'{
if (length($0) > 14) { if (NF>4) print $1 "/" $2 "/.../" $(NF-1) "/" $NF;
else if (NF>3) print $1 "/" $2 "/.../" $NF;
else print $1 "/.../" $NF; }
else print $0;}'"'"')'
PS1='$(eval "echo ${MYPS}")$ '

awk 中的条件语句 if (NF > 4 && length($0) > 14) 只有在当前目录深度超过3个目录并且 $PWD 的长度大于14个字符时才会应用特殊处理,否则它将保持 PS1$PWD

例如:如果当前目录是 ~/workspace/projects/project1$,那么 PS1 将为 ~/workspace/projects/project1$

在你的 .bashrc 文件中加入上述内容后,对你的 PS1 的影响如下:

~$ cd ~/workspace/projects/project1/folder1/test
~/workspace/.../folder1/test$ cd ..
~/workspace/.../project1/folder1$ cd ..
~/workspace/.../project1$ cd ..
~/.../projects$ cd ..
~/workspace$ cd ..
~$

注意当我改变目录时,提示符是如何变化的。如果这不是你想要的,请告诉我。


@pajton:我的答案确实考虑了PS1格式中“pwd_length>14”的条件。请参见上面的 if(NF> 4 && length($0)> 14) 条件,其中14是您的 $pwd_length 值。但是,由于根据您最初的问题,我使用了 pwd[0]/pwd[1]/.../pwd[n-2]/pwd[n-1] PS1格式,因此一旦CWD至少深入4级,我添加了1个子条件来强制执行此 $pwd_length>14 检查。 - anubhava
1
明白了:要改变最终路径的颜色,你可以在左边用例如[\e[0;34m],右边用[\e[0m]$来包围结果,以获得紫色。因此,PS1='[\e[0;34m] $(eval "echo ${MYPS}")[\e[0m]$ '。 - dmonopoly
由于某些原因,我的主目录没有显示~ - Nick Humrich
波浪号需要转义。当我尝试这段代码时,我遇到了同样的问题。 - Sean Perry
此外,可以考虑使用PROMPT_COMMAND而不是eval作为解决方案,以避免每次需要时生成提示。 - Sean Perry
显示剩余6条评论

10
这是基于anubhava的解决方案所使用的内容。它可以设置提示和窗口标题。awk脚本更易读,因此可以轻松地进行调整/定制。
如果路径超过16个字符且深度超过4级,则会折叠该路径。此外,在“…”中还将指示折叠了多少个目录,以便您了解路径有多深,例如:~/usr/..4../path2/path1表示已折叠4级。
# define the awk script using heredoc notation for easy modification
MYPSDIR_AWK=$(cat << 'EOF'
BEGIN { FS = OFS = "/" }
{ 
   sub(ENVIRON["HOME"], "~");
   if (length($0) > 16 && NF > 4)
      print $1,$2,".." NF-4 "..",$(NF-1),$NF
   else
      print $0
}
EOF
)

# my replacement for \w prompt expansion
export MYPSDIR='$(echo -n "$PWD" | awk "$MYPSDIR_AWK")'

# the fancy colorized prompt: [0 user@host ~]$
# return code is in green, user@host is in bold/white
export PS1='[\[\033[1;32m\]$?\[\033[0;0m\] \[\033[0;1m\]\u@\h\[\033[0;0m\] $(eval "echo ${MYPSDIR}")]$ '

# set x/ssh window title as well
export PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME%%.*} $(eval "echo ${MYPSDIR}")\007"'

以下是它的实际效果。绿色的0是上一个命令的返回代码:

enter image description here


5
echo -n $PWD | sed -re "s|(~?/[^/]*/).*(.{$pwd_length})|\1...\2|"

使用-r参数只是为了方便,允许省略括号前面的反斜杠以及将"|"作为分隔符,这样我们就可以在命令中使用斜杠。我猜你的主目录也会显示为~,所以~/foo/bar/baz/应该以~/foo/.../baz结尾,而/foo/bar/baz/应该以/foo/.../baz/结尾。

因此,我们采用可选的~,后跟斜线、名称、斜线作为\1,然后是一些内容,最后剩下的作为\2。


谢谢。它保留了第一个路径组件。但是,它不尊重“/”边界:pajton@dragon:~/workspace/...der1/test$ - pajton
请尝试运行以下命令:echo -n $PWD | sed -re "s|(~?/[^/]*/).*(/.{14,})|\1...\2|",如果它能正常工作,请将14替换为$pwd_length,我会更新我的帖子。 - user unknown

3

还有另一种方法,仍然使用 sedawk 来生成提示符。这将把您的 $HOME 目录转换为 ~,显示您的根目录、最低级别(当前目录)及其父目录,对于每个目录之间用 .. 分隔。

在您的 .bashrc 中(或者在 OS X 上是 .bash_profile):

function generate_pwd {
  pwd | sed s.$HOME.~.g | awk -F"/" '
  BEGIN { ORS="/" }
  END {
  for (i=1; i<= NF; i++) {
      if ((i == 1 && $1 != "") || i == NF-1 || i == NF) {
        print $i
      }
      else if (i == 1 && $1 == "") {
        print "/"$2
        i++
      }
      else {
        print ".."
      }
    }
  }'
}
export PS1="\$(generate_pwd) -> "

该脚本使用awk的内置变量NF(字段数)和位置变量($1,$2 ...)来打印每个字段(目录名称),并使用ORS变量(输出记录分隔符)将它们分隔开。它将内部目录折叠成..以显示在您的提示中。
使用示例:
~/ -> cd Documents/
~/Documents/ -> cd scripts/
~/Documents/scripts/ -> cd test1/
~/../scripts/test1/ -> cd test2
~/../../test1/test2/ -> pwd
/Users/Brandon/Documents/scripts/test1/test2
~/../../test1/test2/ -> cd test3/
~/../../../test2/test3/ -> cd test4/
~/../../../../test3/test4/ -> pwd
/Users/Brandon/Documents/scripts/test1/test2/test3/test4
~/../../../../test3/test4/ -> cd /usr/
/usr/ -> cd local/
/usr/local/ -> cd etc/
/usr/local/etc/ -> cd openssl/
/usr/../etc/openssl/ -> cd private/
/usr/../../openssl/private/ ->

1

Raychi的答案在我的Mac / Catalina上非常有效。

稍微修改了相应部分,以实现“两层深度”开始。

if (length($0) > 16 && NF > 5)
   print $1,$2,$3,".." NF-5 "..",$(NF-1),$NF

结果将类似于:

/Volumes/Work/..3../Python/Tutorials

我的.bashrc文件中相关的部分,作为一个自包含的、可复制粘贴的解决方案如下:

MYPSDIR_AWK=$(cat << 'EOF'
BEGIN { FS = OFS = "/" }
{
   sub(ENVIRON["HOME"], "~");
   if (length($0) > 16 && NF > 5)
      print $1,$2,$3,".." NF-5 "..",$(NF-1),$NF
   else
      print $0
}
EOF
)

export MYPSDIR='$(echo -n "$PWD" | awk "$MYPSDIR_AWK")'

请注意,上述内容不包括Raychi的着色方案。如果需要,请单独添加。

1
除了使用bash内置的PROMPT_DIRTRIM解决方案外,您可能希望尝试$(pwd | tail -c16),它比大多数其他答案都简单一些,但只会给出当前目录的最后16个字符。当然,您可以将16替换为任何您想要的数字。

0
generatePwd(){
  set -- "`pwd | sed -e s.${HOME}.~.g`"
  IFS="/"; declare -a array=($*)
  srt=""
  count=0
  for i in ${!array[@]}; do
      # echo ${array[i]} - $i/${#array[@]}
      if [[ $i == 0 ]]; then
        srt+=""
      elif [[ $i == $((${#array[@]}-1)) ]] || [[ $i == $((${#array[@]}-2)) ]]; then
          srt+="/${array[i]}"
      else
        count=$((${count}+1))
      fi
  done
  if [[ $count != 0 ]]; then
    srt="${array[0]}/.$count.$srt"
  else
    srt="${array[0]}$srt"
  fi
  echo "${srt}"
}

导出 PS1

PS1="\$(generatePwd)"

控制台

$ ~/.3./machine-learning/deep-learning-computer-vision

0
我建议采用我看过的一个小脚本的这种改编方式:
function generate_pwd { 
pwd | awk -F/ 'BEGIN{ ORS="/" } END{for (i=1; i<=NF; i++){print substr($i,1,1)}}' 
}
export PS1="\$(generate_pwd) => "

在你的bashrc文件中添加这几行代码,它就可以正常工作了:

/d/m/r/S/ => pwd
/d/montreui/rpmbuild/SOURCES

0
为什么不直接使用 ${string:position:length} 呢? 你可以使用 ${string:-$max_chars} 来获取字符串最后的 ${max_chars} 个字符。
注意这里使用了负数值。

这是 bash 还是 PHP - Nick Volynkin

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