Kompaktowy monit bash podczas korzystania z drzewa katalogów / nazwy pliku

16

W systemie z Ubuntu 14.04 i bashmam PS1zmienną kończącą się następującą zawartością:

\u@\h:\w\$

tak, że monit pojawi się jako

user@machinename:/home/mydirectory$

Czasami jednak bieżący katalog ma długą nazwę lub znajduje się w katalogach z długimi nazwami, tak więc wygląda to na monit

user@machinename:/home/mydirectory1/second_directory_with_a_too_long_name/my_actual_directory_with_another_long_name$

Spowoduje to wypełnienie linii w terminalu, a kursor przejdzie do innej, co jest denerwujące.

Chciałbym zamiast tego uzyskać coś takiego

user@machinename:/home/mydirectory1/...another_long_name$

Czy istnieje sposób zdefiniowania PS1zmiennej w celu „zawinięcia” i „zagęszczenia” nazwy katalogu, tak aby nigdy nie przekraczać określonej liczby znaków, otrzymując krótszy monit?

BowPark
źródło
1
Na szczęście pamiętam, gdzie przeczytałem, jak dostosować monit powłoki: tldp.org/HOWTO/Bash-Prompt-HOWTO/x783.html Dzięki, dziękuję Gilesowi Orrowi , autorowi Bash Prompt HOWTO i osobom, które się do niego przyczyniły.
wynika
Zobacz także unix.stackexchange.com/a/216871/117549 (oparty na ksh, ale podobny pomysł)
Jeff Schaller

Odpowiedzi:

16

Przede wszystkim możesz po prostu zmienić za \wpomocą \W. W ten sposób wypisywana jest tylko nazwa bieżącego katalogu, a nie cała ścieżka:

terdon@oregano:/home/mydirectory1/second_directory_with_a_too_long_name/my_actual_directory_with_another_long_name $ PS1="\u@\h:\W \$ "
terdon@oregano:my_actual_directory_with_another_long_name $ 

To może wciąż nie wystarczyć, jeśli sama nazwa katalogu jest za długa. W takim przypadku możesz użyć PROMPT_COMMANDdo tego zmiennej. Jest to specjalna zmienna bash, której wartość jest wykonywana jako polecenie przed wyświetleniem każdego monitu. Tak więc, jeśli ustawisz tę funkcję na ustawienie żądanego monitu na podstawie długości ścieżki bieżącego katalogu, możesz uzyskać oczekiwany efekt. Na przykład dodaj te wiersze do ~/.bashrc:

get_PS1(){
        limit=${1:-20}
        if [[ "${#PWD}" -gt "$limit" ]]; then
                ## Take the first 5 characters of the path
                left="${PWD:0:5}"
                ## ${#PWD} is the length of $PWD. Get the last $limit
                ##  characters of $PWD.
                right="${PWD:$((${#PWD}-$limit)):${#PWD}}"
                PS1="\[\033[01;33m\]\u@\h\[\033[01;34m\] ${left}...${right} \$\[\033[00m\] "
        else
                PS1="\[\033[01;33m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] "
        fi


}
PROMPT_COMMAND=get_PS1

Efekt wygląda następująco:

terdon@oregano ~ $ cd /home/mydirectory1/second_directory_with_a_too_long_name/my_actual_directory_with_another_long_name
terdon@oregano /home...th_another_long_name $ 
terdon
źródło
10

Dodanie zwrotu postaci jest moim głównym rozwiązaniem

Mój monit (który zawiera także inne elementy, dzięki czemu jest jeszcze dłuższy) wygląda następująco:

wprowadź opis zdjęcia tutaj

Zauważysz, że $ jest zwracany jako nowa linia

Osiągam to dzięki

HOST='\[\033[02;36m\]\h'; HOST=' '$HOST
TIME='\[\033[01;31m\]\t \[\033[01;32m\]'
LOCATION=' \[\033[01;34m\]`pwd | sed "s#\(/[^/]\{1,\}/[^/]\{1,\}/[^/]\{1,\}/\).*\(/[^/]\{1,\}/[^/]\{1,\}\)/\{0,1\}#\1_\2#g"`'
PS1=$TIME$USER$HOST$LOCATION'\n\$ '

Zauważ, że nawet w osobnym wierszu bardzo długie drzewa katalogów, takie jak

/home/durrantm/Dropbox/96_2013_archive/work/code/ruby__rails są skrócone do

/home/durrantm/Dropbox/_/code/ruby__rails

tzn. „3 górne katalogi / _ / dolne dwa katalogi”, co zwykle mnie obchodzi

Dzięki temu linia nigdy nie będzie zbyt długa z powodu długości drzewa katalogów. Jeśli zawsze chcesz mieć pełne drzewo katalogów, po prostu dostosuj LOCATION, tj

LOCATION=' \[\033[01;34m\]`pwd`'
Michael Durrant
źródło
Więcej na temat koloru na unix.stackexchange.com/q/124407/10043
Michael Durrant
1
Gdzie w rzeczywistości Twój monit zawiera $ ? (Zobacz to .)
G-Man mówi „Reinstate Monica”
Dodano \ $ na końcu, dzięki. to dlatego, że normalnie pokazuję również moją gałąź git, ale to jest za dużo szczegółów na ten temat
Michael Durrant
1
Co? Brak nowej linii? Brak SGR0?
G-Man mówi „Reinstate Monica”
Dodano kanał
Michael Durrant
3

Utworzono ~ / .bash_prompt:

maxlen=36
# set leftlen to zero for printing just the right part of the path
leftlen=19
shortened="..."
# Default PWD
nPWD=${PWD}
if [ ${#nPWD} -gt $maxlen ]; then
  offset=$(( ${#nPWD} - $maxlen + $leftlen ))
  nPWD="${nPWD:0:$leftlen}${shortened}${nPWD:$offset:$maxlen}"
else
  nPWD='\w'
fi
echo "\u@\h:$nPWD\$ "

Dodano w moim ~ / .bash_profile:

function prompt_command {
  export PS1=$(~/.bash_prompt)
}
export PROMPT_COMMAND=prompt_command

Dane wyjściowe to:

user@machinename:/home/mydirectory1/...another_long_name$
kod piekła
źródło
1

Nie rozwiązaniem skracania długich ścieżek, ale wygodnym sposobem uzyskania lepszego przeglądu przy jednoczesnym zachowaniu widoczności wszystkich informacji o ścieżce jest dodanie nowej linii przed ostatnim znakiem. W ten sposób kursor zaczyna się zawsze w tej samej kolumnie, nawet jeśli ścieżka jest wystarczająco długa, aby się owijać, ale okno (okna) konsoli powinny być wystarczająco wysokie, aby nie przewijać zbyt szybko poprzednich linii. Usunąłem kody kolorów dla większej przejrzystości:

murphy@seasonsend:~
$ echo $PS1
\u@\h:\w\n\$
murphy@seasonsend:~
$ 
Murphy
źródło
1

Używam tego, owija się na wiele linii i wcięć o długości, user@hostwięc zakłada, że ​​prąd PS1jest efektywny ' \u@\h:\w$'. Nie obcina ścieżki i dostosowuje się do bieżącej szerokości terminala. Dzieli tylko ścieżkę /, więc elegancko nie radzi sobie z naprawdę długimi katalogami (ale zachowuje przestrzeń do selekcji / kopiowania). Zapewnia to, że zawsze masz do dyspozycji co najmniej 20 znaków.

readonly _PS1="${PS1}" 2>/dev/null

function myprompt()
{
    local IFS
    local nn nb pbits xpwd="" ww=60 len=0 pp='\\w\$ '
    local indent uh="${LOGNAME}@${HOSTNAME//.*/}"

    test -n "$COLUMNS" && let ww=$COLUMNS-20  # may be unset at startup

    PS1="${_PS1}"
    if [ ${#PWD} -ge $ww ]; then
        printf -v indent "%${#uh}s%s" " " "> "  # indent strlen(user@host)

        IFS=/ pbits=( $PWD ); unset IFS
        nb=${#pbits[*]}
        for ((nn=1; nn<nb; nn++)) {
            if [ $(( $len + 1 + ${#pbits[$nn]} )) -gt $ww ]; then
                xpwd="${xpwd}/...\n${indent}..."
                len=0
            fi
            xpwd="${xpwd}/${pbits[$nn]}"
            let len=len+1+${#pbits[$nn]}
        }
        # add another newline+indent if the input space is too tight
        if (( ( ${#uh} + len ) > ww )); then
            printf -v xpwd "${xpwd}\n%${#uh}s" " " 
        fi 
        PS1="${PS1/$pp/$xpwd}$ "    
    fi
}
PROMPT_COMMAND=myprompt

Działa to poprzez usunięcie magii \w(pasuje tylko \w$do tego) PS1i zastąpienie jej $PWD, a następnie zawinięcie w zwykły ciąg znaków. Przelicza się za PS1każdym razem od oryginalnej wartości, która jest zapisana _PS1, co oznacza, że ​​zachowane są również „niewidoczne” znaki ucieczki, mój pełny oryginalny ciąg zachęty xtermi pogrubiony monit:

PS1="\[\033]0;\u@\h:\w\007\]\[$(tput bold)\]\u@\h\[$(tput sgr0)\]:\w$ "

Końcowy wynik to 80-kolumnowy terminal:

mr@onomatopoeia:~$ cd /usr/src/linux/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace
mr@onomatopoeia:/usr/src/linux/tools/perf/scripts/perl/Perf-Trace-Util/lib/...
               > .../Perf/Trace$ _

Działa to od wersji bash-3.2 printf -v var. Ze względu na różne złożoności będzie wymagać dostosowania w przypadku innych odmianPS1 .

(Ścieżka na pasku tytułu Xtermu nie jest ani zawijana, ani skrócona, co można zrobić, włączając jedną z pozostałych odpowiedzi tutaj do powyższej funkcji.)

pan. spuratic
źródło
0

Alternatywnie, w moim .zshrc skracam do pierwszej litery każdego katalogu, jeśli szerokość piksela przekracza określoną szerokość:

user@machinename:/home/mydirectory1/second_directory
user@machinename:/home/mydirectory1/second_directory/my_actual_directory

staje się:

user@machinename:/h/mydirectory1/second_directory
user@machinename:/h/m/s/my_actual_directory

Oto funkcja zsh, aby to zrobić:

     # get the path
     t=`print -P "%m:%~"`;
     t=`echo $t | sed -r 's/([^:])[^:]*([0-9][0-9]):|([^:])[^:]*([^:]):/\1\2\3\4:/'`;
     oldlen=-1;

     # create 4 buckets of letters by their widths
     t1="${t//[^ijlIFT]}";
     t2="${t//[ijlIFTGoQMmWABEKPSVXYCDHNRUw]}";
     t3="${t//[^ABEKPSVXYCDHNRUw]}";
     t4="${t//[^GoQMmW]}";

     # keep abbreviating parent directories in the path until under 456 pixels
     while (( ( ( ${#t1} * 150 ) + ( ${#t2} * 178 ) + ( ${#t3} * 190 ) + ( ${#t4} * 201 ) ) > 4560 && ${#t}!=oldlen)) {
       oldlen=${#t};
       t=`echo $t | sed 's/\/\(.\)[^\/][^\/]*\//\/\1\//'`;
       t1="${t//[^ijlIFT]}";
       t2="${t//[ijlIFTGoQMmWABEKPSVXYCDHNRUw]}";
       t3="${t//[^ABEKPSVXYCDHNRUw]}";
       t4="${t//[^GoQMmW]}";
     };

     PS1=$t

Faktycznie używam tego do aktualizacji tytułu terminalu, dzięki czemu przy wielu kartach mogę prosto sprawdzić, która karta jest która. Pełny plik .zshrc, aby to zrobić, znajduje się tutaj .

Jest to bardzo przydatne, ponieważ utrzymuje kontekst, a w zsh pozwala szybko tabulować uzupełnienie katalogu w tym samym formacie. (np. pisanie cd /h/m/s/<tab>zostanie automatycznie uzupełnione cd /home/mydirectory1/second_directory)

Richard
źródło
Jak to się odnosi do postawionego przez PO pytania, jak zdefiniować monit PS1?
Anthon
Edytowane dla jasności $ t staje się PS1
Richard
0

Spróbuj użyć tego skryptu Python . Odcina poszczególne sekcje nazwy ścieżki, dokładnie tak, jak chciałeś w swoim pytaniu. Wykorzystuje także elipsę Unicode, która zajmuje tylko jedną kolumnę zamiast trzech.

Przykładowe dane wyjściowe dla Twojej ścieżki (gdy podano limit 30 znaków):

/home/mydir…/second…/my_actua

Warto zauważyć, że to rozwiązanie poprawnie obsługuje Unicode w nazwach katalogów za pomocą wcswidth. ${#PWD}, których użyły inne odpowiedzi, źle oceni szerokość wizualną dowolnej ścieżki zawierającej znaki UTF-8.

Functino
źródło