Bash PWD 缩短

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1616678/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 21:18:56  来源:igfitidea点击:

Bash PWD Shortening

linuxbash

提问by ephemient

I'm looking for a bash function that will shorten long path names to keep my PS1 variable from getting excessively long. Something along the lines of:

我正在寻找一个 bash 函数,它可以缩短长路径名,以防止我的 PS1 变量变得过长。类似的东西:

/this/is/the/path/to/a/really/long/directory/i/would/like/shortened

might end up as:

可能最终为:

/t../i../t../p../to/a/r../l../d../i/w../like/shortened

something that the took the path and a maximum acceptable number of characters to shorten to would be perfect for my .bashrc file.

采取路径和最大可接受的字符数来缩短的东西对我的 .bashrc 文件来说是完美的。

采纳答案by John Kugelman

How about a Python script? This shortens the longest directory names first, one character at a time until it meets its length goal or cannot get the path any shorter. It does not shorten the last directory in the path.

Python脚本怎么样?这首先缩短最长的目录名称,一次一个字符,直到达到其长度目标或无法缩短路径。它不会缩短路径中的最后一个目录。

(I started writing this in plain shell script but man, bash stinks at string manipulation.)

(我开始用普通的 shell 脚本编写这个,但是 bash 在字符串操作方面很糟糕。)

#!/usr/bin/env python
import sys

try:
    path   = sys.argv[1]
    length = int(sys.argv[2])
except:
    print >>sys.stderr, "Usage: 
$ echo $DIR
/this/is/the/path/to/a/really/long/directory/i/would/like/shortened
$ ./shorten.py $DIR 70
/this/is/the/path/to/a/really/long/directory/i/would/like/shortened 
$ ./shorten.py $DIR 65
/this/is/the/path/to/a/really/long/direc../i/would/like/shortened
$ ./shorten.py $DIR 60
/this/is/the/path/to/a/re../long/di../i/would/like/shortened
$ ./shorten.py $DIR 55
/t../is/the/p../to/a/r../l../di../i/wo../like/shortened
$ ./shorten.py $DIR 50
/t../is/the/p../to/a/r../l../d../i/w../l../shortened
<path> <length>" sys.exit(1) while len(path) > length: dirs = path.split("/"); # Find the longest directory in the path. max_index = -1 max_length = 3 for i in range(len(dirs) - 1): if len(dirs[i]) > max_length: max_index = i max_length = len(dirs[i]) # Shorten it by one character. if max_index >= 0: dirs[max_index] = dirs[max_index][:max_length-3] + ".." path = "/".join(dirs) # Didn't find anything to shorten. This is as good as it gets. else: break print path

Example output:

示例输出:

_PS1 ()
{
    local PRE= NAME="" LENGTH="";
    [[ "$NAME" != "${NAME#$HOME/}" || -z "${NAME#$HOME}" ]] &&
        PRE+='~' NAME="${NAME#$HOME}" LENGTH=$[LENGTH-1];
    ((${#NAME}>$LENGTH)) && NAME="/...${NAME:$[${#NAME}-LENGTH+4]}";
    echo "$PRE$NAME"
}
PS1='\u@\h:$(_PS1 "$PWD" 20)$ '

回答by ephemient

Doesn't give the same result, but my ~/.bashrccontains

没有给出相同的结果,但我的~/.bashrc包含

#!/bin/bash

begin="" # The unshortened beginning of the path.
shortbegin="" # The shortened beginning of the path.
current="" # The section of the path we're currently working on.
end="${2:-$(pwd)}/" # The unmodified rest of the path.
end="${end#/}" # Strip the first /
shortenedpath="$end" # The whole path, to check the length.
maxlength="${1:-0}"

shopt -q nullglob && NGV="-s" || NGV="-u" # Store the value for later.
shopt -s nullglob    # Without this, anything that doesn't exist in the filesystem turns into */*/*/...

while [[ "$end" ]] && (( ${#shortenedpath} > maxlength ))
do
  current="${end%%/*}" # everything before the first /
  end="${end#*/}"    # everything after the first /

  shortcur="$current"
  shortcurstar="$current" # No star if we don't shorten it.

  for ((i=${#current}-2; i>=0; i--))
  do
    subcurrent="${current:0:i}"
    matching=("$begin/$subcurrent"*) # Array of all files that start with $subcurrent. 
    (( ${#matching[*]} != 1 )) && break # Stop shortening if more than one file matches.
    shortcur="$subcurrent"
    shortcurstar="$subcurrent*"
  done

  begin="$begin/$current"
  shortbegin="$shortbegin/$shortcurstar"
  shortenedpath="$shortbegin/$end"
done

shortenedpath="${shortenedpath%/}" # strip trailing /
shortenedpath="${shortenedpath#/}" # strip leading /

echo "/$shortenedpath" # Make sure it starts with /

shopt "$NGV" nullglob # Reset nullglob in case this is being used as a function.

which limits the path shown to 20 characters max. If the path is over 20 characters, it will be shown like /...d/like/shortenedor ~/.../like/shortened.

这将显示的路径限制为最多 20 个字符。如果路径超过 20 个字符,它将显示为/...d/like/shortened~/.../like/shortened

回答by Evan Krall

Here's a bash-only solution that you might like. This shortens each part of the path down to the shortest prefix that can still be tab-completed, and uses * instead of .. as the filler.

这是您可能喜欢的纯 bash 解决方案。这将路径的每一部分缩短到仍然可以用制表符完成的最短前缀,并使用 * 而不是 .. 作为填充符。

PROMPT_DIRTRIM=3

Give it the length as the first argument, and the path as the optional second argument. If no second argument is given, it uses the current working directory.

给它长度作为第一个参数,路径作为可选的第二个参数。如果没有给出第二个参数,则使用当前工作目录。

This will try to shorten to under the length given. If that's not possible, it just gives the shortest path it can give.

这将尝试缩短到给定的长度以下。如果这是不可能的,它只会给出它可以给出的最短路径。

Algorithmically speaking, this is probably horrible, but it ends up being pretty fast. (The key to quick shell scripts is avoiding subshells and external commands, especially in inner loops.)

从算法上讲,这可能很可怕,但它最终很快。(快速 shell 脚本的关键是避免使用子 shell 和外部命令,尤其是在内部循环中。)

By design, it only shortens by 2 or more characters ('hom*' is just as many characters as 'home').

按照设计,它只会缩短 2 个或更多字符('hom*' 与 'home' 的字符数一样多)。

It's not perfect. There are some situations where it won't shorten as much as is possible, like if there are several files whose filenames share a prefix (If foobar1 and foobar2 exist, foobar3 won't be shortened.)

这并不完美。在某些情况下,它不会尽可能缩短,例如如果有多个文件名共享前缀的文件(如果 foobar1 和 foobar2 存在,则 foobar3 不会被缩短。)

回答by Dmitry Leskov

FYI, there is a built-in \w"shortener" in Bash 4+:

仅供参考,\wBash 4+ 中有一个内置的“缩短器”:

#!/bin/bash

begin="" # The unshortened beginning of the path.
shortbegin="" # The shortened beginning of the path.
current="" # The section of the path we're currently working on.
end="${2:-$(pwd)}/" # The unmodified rest of the path.

if [[ "$end" =~ "$HOME" ]]; then
    INHOME=1
    end="${end#$HOME}" #strip /home/username from start of string
    begin="$HOME"      #start expansion from the right spot
else
    INHOME=0
fi

end="${end#/}" # Strip the first /
shortenedpath="$end" # The whole path, to check the length.
maxlength="${1:-0}"

shopt -q nullglob && NGV="-s" || NGV="-u" # Store the value for later.
shopt -s nullglob    # Without this, anything that doesn't exist in the filesystem turns into */*/*/...

while [[ "$end" ]] && (( ${#shortenedpath} > maxlength ))
do
  current="${end%%/*}" # everything before the first /
  end="${end#*/}"    # everything after the first /

  shortcur="$current"
  shortcurstar="$current" # No star if we don't shorten it.

  for ((i=${#current}-2; i>=0; i--)); do
    subcurrent="${current:0:i}"
    matching=("$begin/$subcurrent"*) # Array of all files that start with $subcurrent. 
    (( ${#matching[*]} != 1 )) && break # Stop shortening if more than one file matches.
    shortcur="$subcurrent"
    shortcurstar="$subcurrent*"
  done

  #advance
  begin="$begin/$current"
  shortbegin="$shortbegin/$shortcurstar"
  shortenedpath="$shortbegin/$end"
done

shortenedpath="${shortenedpath%/}" # strip trailing /
shortenedpath="${shortenedpath#/}" # strip leading /

if [ $INHOME -eq 1 ]; then
  echo "~/$shortenedpath" #make sure it starts with ~/
else
  echo "/$shortenedpath" # Make sure it starts with /
fi

shopt "$NGV" nullglob # Reset nullglob in case this is being used as a function.

will shorten /var/lib/whatever/foo/bar/bazto .../foo/bar/baz.

将缩短/var/lib/whatever/foo/bar/baz.../foo/bar/baz.

回答by bobpaul

I made some improvements to Evan Krall's code. It now checks to see if your path starts in $HOME and begins the shortened variety with ~/ instead of /h*/u*/

我对 Evan Krall 的代码做了一些改进。它现在检查您的路径是否以 $HOME 开头,并以 ~/ 而不是 /h*/u*/ 开始缩短的变体

function tinypwd(){
    PWD=`shortdir`
}

function hugepwd(){
    PWD=`pwd`
}

Also, here are some functions I put in my .bashrc file to shrink the path shown by the shell. I'm not sure if editing $PWD like this is completely safe as some scripts might depend on a valid $PWD string, but so far I haven't had problems with occasional use. Note that I saved the above script as "shortdir" and put it in my PATH.

另外,这里有一些函数我放在我的 .bashrc 文件中以缩小 shell 显示的路径。我不确定像这样编辑 $PWD 是否完全安全,因为某些脚本可能依赖于有效的 $PWD 字符串,但到目前为止我没有遇到偶尔使用的问题。请注意,我将上述脚本保存为“shortdir”并将其放在我的 PATH 中。

#NOTE: trailing space before the closing double-quote (") is a must!!
function tinypwd(){                                                             
    PS1="$(echo $PS1 | sed 's/\w/\`shortdir\`/g') "
}                                                                               

function hugepwd(){                                                             
    PS1="$(echo $PS1 | sed 's/[`]shortdir[`]/\w/g') "                            
} 

EDIT Oct 19 2010

编辑 2010 年 10 月 19 日

The proper way to do the aliases in bash is by modifying the $PS1variable; this is how the prompt is parsed. In MOST cases (99% of the time) the current path is in the prompt string as "\w". We can use sed to replace this with shortdir, like so:

在 bash 中做别名的正确方法是修改$PS1变量;这就是提示的解析方式。在大多数情况下(99% 的情况下),当前路径在提示字符串中为“\w”。我们可以使用 sed 将其替换为shortdir,如下所示:

#!/bin/bash
# Modified from http://stackoverflow.com/a/1617048/359287
# By Alan Christopher Thomas (http://alanct.com)

__pwd_ps1 ()
{
    begin=""
    homebegin=""
    shortbegin=""
    current=""
    end="${2:-$(pwd)}/" # The unmodified rest of the path.
    end="${end#/}" # Strip the first /
    shortenedpath="$end"

    shopt -q nullglob && NGV="-s" || NGV="-u"
    shopt -s nullglob

    while [[ "$end" ]]
    do
      current="${end%%/*}" # Everything before the first /
      end="${end#*/}" # Everything after the first /

      shortcur="$current"
      for ((i=${#current}-2; i>=0; i--))
      do
        [[ ${#current} -le 20 ]] && [[ -z "$end" ]] && break
        subcurrent="${current:0:i}"
        matching=("$begin/$subcurrent"*) # Array of all files that start with $subcurrent
        (( ${#matching[*]} != 1 )) && break # Stop shortening if more than one file matches
        [[ -z "$end" ]] && shortcur="$subcurrent..." # Add character filler at the end of this string
        [[ -n "$end" ]] && shortcur="$subcurrent+" # Add character filler at the end of this string
      done

      begin="$begin/$current"
      homebegin="$homebegin/$current"
      [[ "$homebegin" =~ ^"$HOME"(/|$) ]] && homebegin="~${homebegin#$HOME}" # Convert HOME to ~
      shortbegin="$shortbegin/$shortcur"
      [[ "$homebegin" == "~" ]] && shortbegin="~" # Use ~ for home
      shortenedpath="$shortbegin/$end"
    done

    shortenedpath="${shortenedpath%/}" # Strip trailing /
    shortenedpath="${shortenedpath#/}" # Strip leading /

    [[ ! "$shortenedpath" =~ ^"~" ]] && printf "/$shortenedpath" # Make sure it starts with /
    [[ "$shortenedpath" =~ ^"~" ]] && printf "$shortenedpath" # Don't use / for home dir

    shopt "$NGV" nullglob # Reset nullglob in case this is being used as a function.
}

回答by Alan Christopher Thomas

Here's another spin on Evan's answer:

这是埃文回答的另一个旋转:

enter image description here

在此处输入图片说明

This one uses plus (+) instead of an asterisk (*) for truncated paths. It replaces the HOME path with ~, and it leaves the final directory segment intact. If the final segment is over 20 characters, it shortens it to the tab-completable bit and adds an ellipses (...).

这个使用加号 (+) 而不是星号 (*) 来表示截断的路径。它将 HOME 路径替换为 ~,并保持最终目录段完好无损。如果最后一段超过 20 个字符,它会将其缩短为制表符可完成位并添加一个省略号 (...)。

. ~/.bash_scripts/pwd-prompt.bash

Download the script here and include it in your .bashrc:

在此处下载脚本并将其包含在您的.bashrc

https://raw.github.com/alanctkc/dotfiles/master/.bash_scripts/pwd-prompt.bash

https://raw.github.com/alanctkc/dotfiles/master/.bash_scripts/pwd-prompt.bash

export PS1="[other stuff...] $(__pwd_ps1)$ "

Add the directory to your PS1like this:

PS1像这样将目录添加到您的目录中:

$ echo '/this/is/a/realy/long/path/id/like/shortened' |
 perl -F/ -ane 'print join( "/", map { $i++ < @F - 2 ?
 substr $_,0,3 : $_ } @F)'
/thi/is/a/rea/lon/pat/id/like/shortened

回答by William Pursell

Here's a relatively easy perl solution. This is short enough that you could embed it directly in PS1 rather than invoking a script. It gives all the characters of the truncated names rather than replacing with '.'

这是一个相对简单的 perl 解决方案。这足够短,您可以将它直接嵌入到 PS1 中,而不是调用脚本。它给出了被截断名称的所有字符,而不是用 '.' 替换。

echo '/this/is/a/realy/long/path/id/like/shortened' |
 perl -F/ -ane 'print join( "/", map { m/(.)(.*)/;
 $_ =  . "." x (length  > 2 ? 2 : length  ) if $i++ < @F - 2; $_ } @F)'
/t../i./a/r../l../p../i./like/shortened

I'm not immediately seeing a nice way to replace characters with '.', but here's an ugly way:

我没有立即看到用 '.' 替换字符的好方法,但这是一种丑陋的方法:

PS1='$(pp="$PWD/" q=${pp/#"$HOME/"/} p=${q%?};((${#p}>19))&&echo "${p::9}…${p:(-9)}"||echo "$p") $'

回答by qeatzy

Try this:

尝试这个:

~/.vim/bundle/ack.vim/plugin

It transforms

它变换

.vim/bund…im/plugin

to

/usr/share/doc/xorg-x11-font-utils-7.5/

transfrom

转自

/usr/shar…utils-7.5

to

##代码##

And when $PWDsame as $HOME, show nothing.

$PWD与 相同时$HOME,不显示任何内容。

Bonus: you could modify number of length to fit you need.

奖励:您可以修改长度数量以适合您的需要。