Monit PS1, aby pokazać upływ czasu

10

Obecnie używam tego, aby wyświetlić aktualny czas w wierszu polecenia bash:

PS1=\[\e[0;32m\]\t \W>\[\e[1;37m\]

20:42:23 ~>

Czy można wyświetlić czas, który upłynął od poprzedniego monitu? Jak na przykład:

00:00:00 ~> sleep 10
00:00:10 ~> sleep 20
00:00:20 ~>

To nie ma nic wspólnego z Czy można okresowo zmieniać PS1 za pomocą skryptu w tle?

TeasingDart
źródło
Nie, w tym poście nie ma odpowiedzi i spodziewam się, że monit zmieni się dopiero po wyświetleniu nowego monitu.
TeasingDart
Nie ma realnego sposobu na zrobienie tego, o co prosisz, nie.
DopeGhoti
1
Jest to możliwe, jeśli wyświetlana wartość jest statyczna (pytanie OP nie wydaje się na to pozwalać). Upływający czas można utrzymać, zapisując czas epoki poprzedniego czasu w zmiennej powłoki. Wdrożenie wydaje się jednak dużo pracy (około godziny - być może ktoś zapewni prostsze rozwiązanie niż to, co mam na myśli). To pytanie byłoby pomocne.
Thomas Dickey,

Odpowiedzi:

10

Jednym ze sposobów jest użycie funkcji bash PROMPT_COMMAND do wykonania kodu modyfikującego PS1. Poniższa funkcja to zaktualizowana wersja mojego oryginalnego zgłoszenia; ten używa dwóch mniej zmiennych środowiskowych i poprzedza je „_PS1_”, aby uniknąć blokowania istniejących zmiennych.

prompt_command() {
  _PS1_now=$(date +%s)
  PS1=$( printf "\[\e[0;32m\]%02d:%02d:%02d \W>\[\e[1;37m\] " \
           $((  ( _PS1_now - _PS1_lastcmd ) / 3600))         \
           $(( (( _PS1_now - _PS1_lastcmd ) % 3600) / 60 )) \
           $((  ( _PS1_now - _PS1_lastcmd ) % 60))           \
       )
  _PS1_lastcmd=$_PS1_now
}
PROMPT_COMMAND='prompt_command'
_PS1_lastcmd=$(date +%s)

Umieść to w swoim pliku .bash_profile, aby rozpocząć.

Zauważ, że musisz dość szybko pisać, aby sleepparametr pasował do parametru zachęty - czas naprawdę różni się między monitami, w tym czas potrzebny na wpisanie polecenia.

00:00:02 ~> sleep 5   ## here I typed really quickly
00:00:05 ~> sleep 3   ## here I took about 2 seconds to enter the command
00:00:10 ~> sleep 30 ## more slow typing
00:01:35 ~>

Późne dodanie:

W oparciu o teraz usuniętą odpowiedź @Cyrus, oto wersja, która nie zaśmieca środowiska dodatkowymi zmiennymi:

PROMPT_COMMAND='
    _prompt(){
        PROMPT_COMMAND="${PROMPT_COMMAND%-*}-$SECONDS))\""
        printf -v PS1 "\[\e[0;32m\]%02d:%02d:%02d \W>\[\e[1;37m\] " \
                      "$(($1/3600))" "$((($1%3600)/60))" "$(($1%60))"
    }; _prompt "$((SECONDS'"-$SECONDS))\""

Dodatkowe opóźnienie:

Począwszy od wersji bash 4.2 ( echo $BASH_VERSION), możesz uniknąć datewywołań zewnętrznych za pomocą nowego ciągu formatu printf; zamień $(date +%s)elementy na $(printf '%(%s)T' -1). Począwszy od wersji 4.3 , można pominąć -1parametr polegający na zachowaniu „brak argumentu teraz ”.

Jeff Schaller
źródło
To jest naprawdę blisko. Działa, gdy kopiuję / wklejam z monitu bash, ale kiedy próbowałem dodać go do mojego .bashrc, drukuje „1451424431: polecenie nie znaleziono”
TeasingDart
może trochę za dużo zostało skopiowane / wklejone?
Jeff Schaller
Ta ostatnia wersja działała dokładnie tak, jak chciałem! Myślę, że miało to coś wspólnego z moją pułapką, aby ustawić kolor tekstu po monicie. Dziękuję Ci.
TeasingDart
po zresetowaniu $SECONDSprzestaje on śledzić czas od uruchomienia powłoki,
mikeserv
1
@chepner - cóż, jasne, ale to nigdy nie utrzymuje czasu powłoki. nie zrozumcie mnie źle - głosowałem za tym, ponieważ jest to dobra odpowiedź - ale myślę, że przedefiniowanie powłoki interaktywnej $SECONDSdla każdego monitu może wywołać nieoczekiwane zachowania. każda inna funkcja powłoki, która mogłaby jej użyć z jakiegokolwiek powodu związanego z oceną czasu wykonywania, będzie działała nieprawidłowo.
mikeserv
4
PS1[3]=$SECONDS
PS1='${PS1[!(PS1[1]=!1&(PS1[3]=(PS1[2]=$SECONDS-${PS1[3]})/3600))
   ]#${PS1[3]%%*??}0}$((PS1[3]=(PS1[2]/60%60),  ${PS1[3]})):${PS1[1
   ]#${PS1[3]%%*??}0}$((PS1[3]=(PS1[2]%60),     ${PS1[3]})):${PS1[1
   ]#${PS1[3]%%*??}0}$((PS1[3]=(SECONDS),       ${PS1[3]})):'$PS1

Obsługuje formatowanie według obliczeń - więc, chociaż rozszerza się kilka razy, nie robi żadnych podpowłok ani potoków.

Po prostu traktuje $PS1jak tablicę i wykorzystuje wyższe indeksy do przechowywania / obliczania dowolnego / wszystkich niezbędnych stanów między podpowiedziami. Nie ma to wpływu na żaden inny stan powłoki.

00:00:46:[mikeserv@desktop tmp]$
00:00:01:[mikeserv@desktop tmp]$
00:00:00:[mikeserv@desktop tmp]$
00:00:01:[mikeserv@desktop tmp]$
00:00:43:[mikeserv@desktop tmp]$ sleep 10
00:00:33:[mikeserv@desktop tmp]$ sleep 10
00:00:15:[mikeserv@desktop tmp]$
00:00:15:[mikeserv@desktop tmp]$
00:00:02:[mikeserv@desktop tmp]$
00:02:27:[mikeserv@desktop tmp]$

Mogę to trochę zepsuć ...

Najpierw zapisz bieżącą wartość $SECONDS:

PS1[3]=$SECONDS

Następnie zdefiniuj, $PS1[0]aby był rekurencyjny w sposób, który zawsze ustawi odpowiednie wartości, $PS1[1-3]a jednocześnie będzie zawierał odnośniki. Aby uzyskać tę część, musisz wziąć pod uwagę kolejność, w jakiej wyrażenia matematyczne są obliczane. Co najważniejsze, matematyka w powłoce jest zawsze ostatnim rodzajem biznesu dla matematyki w powłoce. Przede wszystkim powłoka rozszerza wartości. W ten sposób możesz odwoływać się do starej wartości zmiennej powłoki w wyrażeniu matematycznym po przypisaniu jej za pomocą $.

Oto pierwszy prosty przykład:

x=10; echo "$(((x+=5)+$x+x))" "$x"

40 15

Powłoka oceni tę instrukcję, najpierw podstawiając wartość $xwszędzie tam, gdzie $używane jest odwołanie do znaku dolara, a zatem wyrażenie staje się:

(x+=5)+10+x

... wtedy powłoka dodaje 5 do wartości, $xa następnie rozwija całe wyrażenie x+10+x, zachowując tylko faktycznie przypisaną wartość w zmiennej referencyjnej. Zatem rozszerzona wartość wyrażenia matematycznego wynosi 40, ale ostateczna wartość $xto 15.

Tak w dużej mierze $PS1działa również równanie, z tym wyjątkiem, że w indeksach tablicowych wykorzystywany jest dalszy poziom rozszerzenia / oceny matematyki.

PS1='${PS1[!(PS1[1]=!1&(...))]#...}...'

Nie jestem do końca pewien, dlaczego zdecydowałem się PS1[1]=!1tam użyć - wydaje mi się, że była to po prostu głupia estetyka - ale przypisuje to 0 $PS1[1]podczas rozszerzania go do podstawiania parametrów. Wartość bitowego AND dla 0 i cokolwiek innego zawsze będzie wynosić 0, ale nie zwiera się jak wartość logiczna &&, gdy skrajnie lewe pierwsze to 0, więc wyrażenie w nawiasie wciąż jest oceniane za każdym razem. Jest to oczywiście ważne, ponieważ w pierwszej elipsie $PS1[2,3]ustawiane są wartości początkowe .

W każdym razie $PS1[1]zapewnione jest, że wynosi 0, nawet jeśli jest sfałszowane w / między kolejnymi losowaniami. W nawiasach znajdują się ...

PS1[3]=(PS1[2]=$SECONDS-${PS1[3]})/3600

... $PS1[2]przypisano różnicę $PS1[3]i $SECONDSoraz $PS1[3]przypisano mu iloraz tej wartości i 3600. Wszystkie wartości są tutaj inicjowane. A więc:

${PS1[1]#${PS1[3]%%*??}0}

... jeśli są co najmniej dwie cyfry, $PS1[3]wówczas wewnętrzne rozwinięcie jest zerowe, a ponieważ wiemy, że $PS1[1]wynosi 0, to jeśli $PS1[3]można je zastąpić niczym, to także jest $PS1[1]ono rozwinięte do wartości. W ten sposób tylko wartości jednocyfrowe dla każdej iteracji $PS1[3]przypisań zwiększą początkowe zero, a $PS1[3]samo jest bezpośrednio rozszerzone modulo 60 zaraz po tym, jednocześnie przypisując kolejną kolejno mniejszą wartość dla każdej godziny, minuty, sekundy.

Opłucz i powtórz, aż do ostatniej iteracji, w której $PS1[3]zostanie nadpisana w / bieżąca wartość, $SECONDSaby można ją było porównać z $SECONDSjeszcze raz, gdy zostanie narysowany monit.

mikeserv
źródło
1

Najlepsze rozwiązanie, jakie do tej pory znalazłem, to: https://github.com/jichu4n/bash-command-timer

Który drukuje [ 1s011 | May 25 15:33:44 BST ]aka czas, który upłynął po prawej stronie po wykonaniu polecenia, więc nie zaśmieca PS1.

Cały ciąg i format czasu można konfigurować. Nawet kolor i precyzja są konfigurowalne. Wiem, że może to być trochę dla niektórych minimalistów, ale jest całkiem fajne.

sebs
źródło