Invite PS1 pour afficher le temps écoulé

10

J'utilise actuellement ceci pour afficher l'heure actuelle dans mon invite bash:

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

20:42:23 ~>

Est-il possible d'afficher le temps écoulé depuis l'invite précédente? Tel que:

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

Cela n'a rien de commun avec Est-il possible de changer périodiquement la PS1 par un script en arrière-plan?

TeasingDart
la source
Non, il n'y a pas de réponse dans ce message et je m'attends à ce que l'invite ne change que lorsqu'une nouvelle invite s'affiche.
TeasingDart
Il n'y a pas vraiment de moyen réalisable de faire ce que vous demandez, non.
DopeGhoti
1
C'est faisable si la valeur affichée est statique (la question de OP ne semble pas le permettre). Le temps écoulé peut être conservé en enregistrant le temps de l'époque de l'heure précédente dans une variable shell. L'implémenter semble cependant beaucoup de travail (une heure environ - peut-être que quelqu'un fournira une solution plus simple que ce que j'ai en tête). Cette question serait utile.
Thomas Dickey

Réponses:

10

Une façon de le faire serait d'utiliser la fonctionnalité PROMPT_COMMAND de bash pour exécuter du code qui modifie PS1. La fonction ci-dessous est une version mise à jour de ma soumission d'origine; celui-ci utilise deux variables d'environnement en moins et les préfixe avec "_PS1_" pour essayer d'éviter d'encombrer les variables existantes.

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)

Mettez-le dans votre .bash_profile pour démarrer.

Notez que vous devez taper assez rapidement pour que le sleepparamètre corresponde au paramètre d'invite - le temps est vraiment la différence entre les invites, y compris le temps qu'il vous faut pour taper la commande.

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 ~>

Ajout tardif:

Basé sur la réponse maintenant supprimée de @Cyrus, voici une version qui n'encombre pas l'environnement avec des variables supplémentaires:

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))\""

Ajout tardif supplémentaire:

À partir de la version bash 4.2 ( echo $BASH_VERSION), vous pouvez éviter les dateappels externes avec une nouvelle chaîne de format printf; remplacer les $(date +%s)pièces par $(printf '%(%s)T' -1). À partir de la version 4.3 , vous pouvez omettre le -1paramètre pour vous fier au comportement "aucun argument ne signifie maintenant ".

Jeff Schaller
la source
C'est très proche. Cela fonctionne lorsque je copie / colle à partir d'une invite bash, mais lorsque j'essaye de l'ajouter à mon .bashrc, il affiche "1451424431: commande introuvable"
TeasingDart
peut-être un peu trop copié / collé?
Jeff Schaller
Cette dernière version a fonctionné, exactement ce que je voulais! Je pense que cela avait quelque chose à voir avec mon piège pour définir la couleur du texte après l'invite. Je vous remercie.
TeasingDart
lorsque vous le réinitialisez, $SECONDSil cesse de suivre le temps écoulé depuis le démarrage du shell,
mikeserv
1
@chepner - enfin, bien sûr, mais cela ne garde plus jamais l'heure du shell. ne vous méprenez pas - j'ai voté pour parce que c'est une bonne réponse - mais je pense que la redéfinition d'un shell interactif $SECONDSpour chaque invite est susceptible de provoquer des comportements inattendus. toute autre fonction shell qui pourrait l'utiliser pour une raison quelconque associée à l'évaluation de l'exécution se comportera mal.
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

Cela gère la mise en forme par calcul - donc, bien qu'il se développe plusieurs fois, il ne fait aucun sous-shell ou tuyau.

Il traite simplement $PS1comme un tableau et utilise les indices supérieurs pour stocker / calculer tout / tous les états nécessaires entre les invites. Aucun autre état shell n'est affecté.

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]$

Je peux le décomposer un peu peut-être ...

Tout d'abord, enregistrez la valeur actuelle de $SECONDS:

PS1[3]=$SECONDS

Ensuite, définissez $PS1[0]comme auto-récursif d'une manière qui définira toujours les bonnes valeurs $PS1[1-3]tout en se référençant simultanément. Pour obtenir cette partie, vous devez considérer l'ordre dans lequel les expressions shell-math sont évaluées. Plus important encore, le shell-math est toujours le dernier ordre du jour pour le shell-math. Avant tout, le shell développe les valeurs. De cette façon, vous pouvez référencer une ancienne valeur pour une variable shell dans une expression mathématique après l'avoir affectée à l'aide de $.

Voici d'abord un exemple simple:

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

40 15

Le shell évaluera cette déclaration en substituant d'abord la valeur de l' $xendroit où la $référence du signe dollar est utilisée, et ainsi l'expression devient:

(x+=5)+10+x

... puis le shell ajoute 5 à la valeur de $xet étend ensuite l'expression entière à x+10+x, tout en ne conservant que la valeur réellement affectée dans la variable de référence. Et donc la valeur développée de l'expression mathématique est 40, mais la valeur ultime de $xest 15.

C'est en grande partie ainsi que fonctionne l' $PS1équation, sauf qu'il existe un autre niveau d'expansion / évaluation mathématique exploité dans les indices de tableau.

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

Je ne sais pas vraiment pourquoi j'ai choisi de l'utiliser PS1[1]=!1ici - je suppose que c'était probablement juste une esthétique stupide - mais cela attribue 0 à $PS1[1]tout en le développant pour la substitution de paramètres. La valeur d'un AND au niveau du bit pour 0 et toute autre chose sera toujours 0, mais elle ne court-circuite pas comme le fait un booléen &&lorsque le primaire le plus à gauche est 0 et donc l'expression entre parenthèses est toujours évaluée à chaque fois. C'est important, bien sûr, parce que cette première élipse est l'endroit où les valeurs initiales de $PS1[2,3]sont définies.

Quoi qu'il en soit, $PS1[1]est ici assuré d'être égal à 0 même s'il est falsifié avec / entre les tirages d'invite. Entre parenthèses, il ...

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

... $PS1[2]se voit attribuer la différence de $PS1[3]et $SECONDS, et $PS1[3]le quotient de cette valeur et 3600. Toutes les valeurs sont ici initialisées. Et donc:

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

... s'il y a au moins deux chiffres, $PS1[3]alors l'expansion interne est nulle, et parce que nous savons que $PS1[1]0, alors si elle $PS1[3]peut être remplacée par rien, il en est de même $PS1[1]autrement elle est développée à sa valeur. De cette façon, seules les valeurs à un chiffre pour chaque itération d' $PS1[3]affectations développeront un zéro de tête, et $PS1[3]elle-même est étendue modulo 60 immédiatement après tout en étant simultanément affectée à la prochaine valeur successivement plus petite pour chacune des heures, minutes et secondes.

Rincez et répétez, jusqu'à ce que la dernière itération $PS1[3]soit écrasée avec la valeur actuelle de $SECONDSafin qu'elle puisse être comparée $SECONDSune fois de plus lorsque l'invite est ensuite dessinée.

mikeserv
la source
1

La meilleure solution que j'ai trouvée jusqu'à présent est la suivante: https://github.com/jichu4n/bash-command-timer

Qui imprime [ 1s011 | May 25 15:33:44 BST ]alias le temps écoulé sur le côté droit après la commande exécutée, de sorte qu'il ne vous encombre pas PS1.

L'ensemble du format de chaîne et d'heure est configurable. Même la couleur et la précision sont configurables. Je sais que cela pourrait être un peu trop pour certains minimalistes, mais c'est plutôt cool.

sebs
la source