I am using Ubuntu and I am tired of this long prompts in bash when I am working with some deep directory hierarchy. So, I would like to tweak my PS1 to shorten the working directory part the following way:
Currently I have:
pajton@dragon:~/workspace/projects/project1/folder1/test$
开发者_JAVA技巧
and would like to have:
pajton@dragon:~/workspace/.../folder1/test$
The truncating would occur if len($PWD) passes given threshold. I want to always keep the first path component and at least one last path component. Then as space permits, add more components taking from the right.
This is what I have currently. It works, but: 1) doesn't keep first path component, 2) doesn't respect cutting path at boundaries:
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
And the result:
pajton@dragon:...sth/folder1/sample$
Thanks in advance!
For people looking for a much simpler solution and don't need the name of the first directory in the path, Bash has built-in support for this using the PROMPT_DIRTRIM
variable. From the documentation:
PROMPT_DIRTRIM
If set to a number greater than zero, the value is used as the number of trailing directory components to retain when expanding the \w and \W prompt string escapes (see Printing a Prompt). Characters removed are replaced with an ellipsis.
For example:
~$ 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$
Downside: It depends on the directory level, not the length of the path, which you might not want.
Upside: It's very simple. Just add export PROMPT_DIRTRIM=2
to your .bashrc
.
Consider this script using awk instead of sed for your case:
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}$ '
For your example of directory ~/workspace/projects/project1/folder1/test
it makes PS1 as: ~/workspace/.../folder1/test
UPDATE
Above solution will set your prompt but as you noted in your comment that it will NOT change PS1 dynamically when you change directory. So here is the solution that will dynamically set PS1 when you change directories around.
Put these 2 lines in your .bashrc file:
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}")$ '
if (NF > 4 && length($0) > 14)
condition in awk will only apply special handling when your current directory is more than 3 directories deep AND if length of $PWD
is more than 14 characters otherwise and it will keep PS1 as $PWD
.
eg: if current directory is ~/workspace/projects/project1$
then PS1 will be ~/workspace/projects/project1$
Effect of above in .bashrc will be as follows on your PS1:
~$ cd ~/workspace/projects/project1/folder1/test
~/workspace/.../folder1/test$ cd ..
~/workspace/.../project1/folder1$ cd ..
~/workspace/.../project1$ cd ..
~/.../projects$ cd ..
~/workspace$ cd ..
~$
Notice how prompt is changing when I change directories. Let me know if this is not what you wanted.
This is what I use based on the solutions from anubhava. It sets both the prompt and the windows title. The awk script is more readable so it can be tweaked/customized easily.
It will fold the path if it's more than 16 chars and 4 levels deep. Furthermore, it will also indicate in the ... how many directories were folded, so you get a sense of how deep the path is, ie: ~/usr/..4../path2/path1
indicates 4 levels were folded.
# 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"'
Here's what it looks like in action. The green 0 is the return code of last command:
echo -n $PWD | sed -re "s|(~?/[^/]*/).*(.{$pwd_length})|\1...\2|"
sed with -r only for convenience, allows to omit backslash before parentheses, and "|" as delimiter only for convenience too - because we want to use the slash inside the command. I guess your home get's displayed as ~ as well, so ~/foo/bar/baz/ should end in ~/foo/.../baz, and /foo/bar/baz/ as /foo/.../baz/.
So we take an optional ~, followed by slash, name, slash as \1, then something, then the rest as \2.
Another approach, still using sed
and awk
to generate the prompt. This will convert your $HOME
directory into ~, show you your root directory, your lowest level (current directory), and its parent, separated by ..
for each directory in between.
Inside of your .bashrc
(or .bash_profile
on OS X):
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) -> "
The script uses awk's built in NF
variable (number of fields) and positional variables ($1, $2 ...
) to print each field (directory name) separated by the ORS
variable (output record separator). It collapses the inner directories into ..
in your prompt.
Example of it in use:
~/ -> 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/ ->
Apart from the bash-builtin solution using PROMPT_DIRTRIM
, you may want to try $(pwd | tail -c16)
, which is a tad simpler than most other answers, but just gives the last 16 characters of the current directory. Of course replace 16 by any number you want.
Why not just use ${string:position:length}
?
You can do ${string:-$max_chars}
to have the last ${max_chars}
of the string.
note the negative value
Not so different from previous solutions. However, maybe a bit more readable/editable. However, no solution to the folder name boundary, only focusing on the length of the prompt.
### SET MY PROMPT ###
if [ -n "$PS1" ]; then
# A temporary variable to contain our prompt command
NEW_PROMPT_COMMAND='
pwd_short=${PWD/#$HOME/\~};
if [ ${#pwd_short} -gt 53 ]; then
TRIMMED_PWD=${pwd_short: 0: 25}...${pwd_short: -25}
else
TRIMMED_PWD=${pwd_short}
fi
'
# If there's an existing prompt command, let's not
# clobber it
if [ -n "$PROMPT_COMMAND" ]; then
PROMPT_COMMAND="$PROMPT_COMMAND;$NEW_PROMPT_COMMAND"
else
PROMPT_COMMAND="$NEW_PROMPT_COMMAND"
fi
# We're done with our temporary variable
unset NEW_PROMPT_COMMAND
# Set PS1 with our new variable
# \h - hostname, \u - username
PS1='\u@\h: $TRIMMED_PWD\$ '
fi
added to the .bashrc file. All parts of the prompt is updated properly. The first part is shortened if you're in your home directory. Example:
user@computer: ~/misc/projs/solardrivers...src/com/mycompany/handles$
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}"
}
export PS1
PS1="\$(generatePwd)"
Console
$ ~/.3./machine-learning/deep-learning-computer-vision
https://github.com/chrissound/SodiumSierraStrawberry
Allows you to truncate a path like:
From: /home/sodium/Projects/Personal/Sierra/Super/Long/Path/HolyAvacado
To: »Projects/Sie…/Sup…/Lon…/Pat…/HolyAvacado/
Raychi's answer works great for me on Mac / Catalina.
Modified the corresponding section slightly for a "two deep" beginning.
if (length($0) > 16 && NF > 5)
print $1,$2,$3,".." NF-5 "..",$(NF-1),$NF
Result will be similar to:
/Volumes/Work/..3../Python/Tutorials
The related part of my .bashrc file as a self contained, copy & paste solution is:
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")'
Note that the above does not include Raychi's colorization scheme. Add this separately if you need it.
I suggest this adaptation of a small script I've seen :
function generate_pwd {
pwd | awk -F/ 'BEGIN{ ORS="/" } END{for (i=1; i<=NF; i++){print substr($i,1,1)}}'
}
export PS1="\$(generate_pwd) => "
You add these few lines in your bashrc and it works... normally :
/d/m/r/S/ => pwd
/d/montreui/rpmbuild/SOURCES
精彩评论