Avertissement nouveau mail dans prompt bash

Bonjour,

Je me souviens que fran.b avait réussi à être averti de nouveaux mails en l’affichant dans le prompt de la ligne de commandes (avant le $). Mais je n’arrive plus à retrouver où …

Et je ne vois pas comment faire car la variable PS1 est évaluée une seule fois au login. Si elle appelle un script ce script n’est évalué qu’une fois.

Quelqu’un as une idée ?


J’ai trouvé un moyen d’évaluer le script à chaque affichage du prompt ici : mail alert in bash prompt
En fait il faut faire :

Je vais essayer de faire un script qui écrit dans un fichier “3 nouveaux mails” quand on a reçu 3 mails et qui l’efface quand il n’y a pas de mails. Ainsi il suffit de mettre dans PS1 quelque chose du genre

Mais cette solution ne me satisfait pas vraiment. Pensez-vous qu’on peut avoir une solution qui ne fasse pas un accès au disque dur à chaque affichage du prompt ? J’aimerai arriver à stocker l’information dans une variable plutôt que dans un fichier.


Apparament c’est impossible, les variables globales se font grâce à export, mais ça ne permet pas de transmettre les variables à tous les shells ouverts (uniquement au shell courant). Donc il faudrait faire un démon ?

Bonsoir,
La valeur de PS1 n’est pas “fixe”, elle est évaluée à chaque affichage de la ligne d’invite.
Pour la rendre plus dynamique, tu peux exploiter la variable d’environnement PROMPT_COMMAND.
Exemple : PROMPT_COMMAND=‘maFonction’ ou PROMPT_COMMAND=‘monScript’

maFonction et monScript seront alors exécuté à chaque affichage de la ligne d’invite.
Tu peux alors mettre tout ce que tu veux dans la fonction ou le script pour qu’ils modifient la variable PS1.

Il y a quelques temps de cela, je me suis fait ce script pour afficher le nombre de mail que je n’ai pas récupéré et qui sont non lus. Il se base sur fetchmail.
Tu peux peut-être te baser dessus pour faire ce que tu souhaites :

#!/bin/bash

ROUGE="\e[31;1m"
VERT="\e[32;1m"
JAUNE="\e[33;1m"
BLEU="\e[34;1m"
ROSE="\e[35;1m"
CYAN="\e[36;1m"
BLANC="\e[37;1m"
RESET="\e[0m"

getBoxes()
{
  unset boxes counters
  maxLenName=0
  maxDigit=1
  while read 
  do
    [[ "${REPLY}" = *passage* ]] && {
	  box="${REPLY#*[\"«]}"
	  box="${box%[\"»]*}"
	  boxes+=( "${box}" )
	  counters+=( '?' )
	  
	  (( maxLenName < ${#box} )) && maxLenName=${#box} 
	}
  done < <(fetchmail --version)
  displayCounters
}

formatStrings()
{
	for((index=0; index<${#counters[@]};index++))
	do
	  counter=${counters[${index}]}
	  spaceCount=$((${maxDigit}-${#counter}+1))
	  
	  counters[${index}]="$(printf "%s%*c" ${counter} ${spaceCount})"
	done
}

getCounters()
{
	 unset counters
	 maxDigit=0
	 while read
	 do
	   counter=0
	   [[ "${REPLY}" = [0-9]* ]] && counter=${REPLY%% *}

	   counters+=( ${counter} )
	   (( ${#counter} > maxDigit ))  && maxDigit=${#counter}
	 done < <(fetchmail -ck)
	 formatStrings
}

displayCounters()
{
	maxCols=$(tput cols) 	
	# récupération de la position du curseur
	echo -en "\e[s"
	
	# on se positionne en haut
	echo -en "\e[H"
	
	for((index=0; index<${#counters[@]};index++))
	{
		counter="${counters[${index}]}"
		box="${boxes[${index}]}"
		nbSpace=$(( maxLenName - ${#boxes[${index}]} ))
		string="$(eval "printf '%.0s ' {0..${nbSpace}}")${box} : ${counter}"
		display="$(eval "printf '%.0s ' {0..${nbSpace}}")${JAUNE}${box}${RESET} : ${CYAN}${counter}${RESET}"
		
		echo -e "\r\033[$((${maxCols}-${#string}))C${display}"
	}

	# on repositionne le curseur
	echo -en "\e[u"
}

while :
do
getBoxes
getCounters
displayCounters
sleep 600
done

Intéressante cette variable d’environnement ! merci. Ca m’offre une alternative pour rendre la variable PS1 dynamique.

Je ne comprend pas bien ton script car je ne connaîs pas ce genre de choses en bash : ${#counters[@]}. Est-ce que ta méthode permet de modifier les prompt de tous les shells ouverts d’un utilisateur ? (à la fois tty1, tty2, …)

[parenthèse]Attention, j’ai oublié de préciser. Cette variable PROMPT_COMMAND n’existe pas en dash.

Cela dit, si tu utilises dash en tant que shell, tu peux très bien utiliser la variable PS1 en lui demandant d’exécuter une fonction. (ex. PS1=’$(maFonction)’). Le prompt affiché sera alors le résultat renvoyé par maFonction.

Mais le titre de ton post indique que c’est du bash…
[/parenthèse]

Pour que le prompt soit impacté pour toutes les terminaux ouverts par un utilisateur, il suffit de valoriser PROMPT_COMMAND dans le fichier ~/.bashrc de l’utilisateur (modification prise en compte pour toute nouvelle session et non pour les sessions actives).

Pour mon script : Il n’affiche rien dans le prompt. Il affiche le nom et le nombre de mail non lus de chaque mailbox dans le coin supérieur droit du terminal (actualisation toutes les 5 minutes --> 600 sec).

la notation ${#counters[@]} retourne le nombre d’éléments du tableau counters.

plus d’informations sur les tableaux

Donc si j’ouvre 10 shells, le client mail va être lancé 10 fois toutes les 5 minutes ?

alors oui, effectivement fetchmail sera interrogé toutes les 5 minutes pour chaque script lancé.
mais bon, ça ne doit pas bien être compliqué de faire un petit script de type serveur qui sera le seul à lancer fetchmail.

Je crois que ça va être dur de ne pas faire un accès au disque dur à chaque affichage du prompt. Il faudrait faire un démon qui, quand on l’intéroge, renvoie un entier indiquant le nombre de nouveaux mails (toutes boites confondues). Le problème, c’est la communication entre ce démon et l’affectation de la variable PS1 du type

On peut récupérer le PID du démon, mais après … que faire ? Peut-être un pipe, un fifo, ou des signaux inter-processus. Mais à première vue les fifos sont des fichiers et donc nécessitent un accès au disque dur.

regarde du coté de netcat (nc pour les intimes) :wink:

Si j’ai bien compris, je devrais choisir un port arbitraire (le 732), je fais écouter ce port par mon démon vérificateur de mails. Et la fonction shell qui s’exécute lors de l’évaluation de la variable PS1 intéroge le port 732 puis attend la réponse du démon.

Comment la fonction du shell va-t-elle intéroger le port 732 sans utiliser le disque dur ? (c’est-à-dire sans faire une commande externe au shell, parce que pour faire un ls par exemple, il faut charger le binaire depuis le disque dur dans la mémoire). S’il faut faire un accès disque, il est plus simple d’utiliser un fichier comme moyen de communication.

si tu ne souhaites pas utiliser nc pour récupérer l’info, tu peux utiliser les méchanismes bash :

read < /dev/tcp/localhost/732 devrait faire l’affaire pour lire et echo “une demande” > /dev/tcp/localhost/732 pour écrire !

Certe, ça s’apparente à un fichier mais bon, on ne peut pas échaper aux accès disque !

:118 joli ! Maintenant je sais écouter un port avec le shell. Si on pouvait enregistrer l’emplacement du port dans une variable, et ne plus interoger le système de fichier par la suite, cette solution serait bonne.

En tout cas tu m’as l’air vachement calé avec le shell

Je viens de tomber sur le man de fifo. Et ils disent que les informations échangées ne passent pas par le disque dur. Et une fois qu’un fifo est créé, on peut s’y référer via son nom qui est une variable (donc pas d’accès disque). J’ai trouvé un exemple d’utilisation de fifo.

Il ne me reste plus qu’à modifier la commande exit pour qu’elle ferme le fifo créé dans le .bashrc et ça devrait rouler.

[quote=“branch”]
Il ne me reste plus qu’à modifier la commande exit pour qu’elle ferme le fifo créé dans le .bashrc et ça devrait rouler.[/quote]
pas besoin.
il te suffit de rajouter ceci à ton .bashrc
trap 'rm ’ EXIT

Pas mal non plus, je connaissais pas. Mais de toutes façons je me suis trompé, j’ai trouvé un autre exemple d’utilisation de fifo, et il s’avère qu’on se réfère à un fifo via son chemin dans le système de fichier.

Tant pis, je vais faire mon idée du début (à savoir communication par simple fichier). Au moins c’est facile à faire.

je ne saisi pas trop ce que tu entends par “on se réfère à un fifo via son chemin dans le système de fichier”

et ceci :

monFifo=/tmp/$$.fifo
[ ! -p "${monFifo}" ] && mkfifo "${monFifo}"
exec 3<>"${monFifo}"

echo "pas besoin d'un chemin pour spécifier le fifo" >&3
read -u3

echo "${REPLY}"

rm "${monFifo}"

Ok ça m’as l’air pas mal.

Par contre, quand on envoie quelque chose dans un fifo, ça bloque (on attend que quelqu’un le lise). Donc il va me falloir deux démons : un qui envoie en permanence l’information sur le fifo, et l’autre qui va voir s’il y a des nouveaux mails toutes les cinq minutes. Bien entendu, à chaque fois que le premier démon doit communiquer l’information sur le fifo, il consulte le deuxième pour voir s’il y a du neuf.

Pour faire cette consultation, il faut obligatoirement utiliser quelquechose de non synchrone, pour ne pas bloquer le démon qui active le client mail toutes les cinq minutes. Donc pas de fifo. Ce coup ci, je peux utiliser un fichier normal pour communiquer (car ça n’affecte pas la vitesse à laquelle le prompt s’affiche).

[quote=“branch”]Ok ça m’as l’air pas mal.

Par contre, quand on envoie quelque chose dans un fifo, ça bloque (on attend que quelqu’un le lise)[/quote]

Pas si tu utilises les fd tel que je l’ai fait. Le exec 3<>/tmp/$$.fifo fait toute la différence. Il permet d’ouvrir le pipe en lecture/écriture de façon permanente. Et c’est le fait qu’il soit ouvert que ça ne bloque pas.

note :
pour le fermer : exec 3<&- 3>&-

edit : Tant que j’y pense. Il se peut que le fd 3 soit déjà utilisé. Il faut donc prendre le premier non utilisé.
Voici comment faire :

read < <(join --nocheck-order -v2 <(printf "%s\n" /proc/$$/fd/*) <(eval "printf '/proc/'$$'/fd/%s\n' {3..$(ulimit -n)}"))
monFD=${REPLY##*/}

Ensuite, pour l’utiliser :

monFifo=/tmp/$$.fifo
[ ! -p "${monFifo}" ] && mkfifo "${monFifo}"

read < <(join --nocheck-order -v2 <(printf "%s\n" /proc/$$/fd/*) <(eval "printf '/proc/'$$'/fd/%s\n' {0..$(ulimit -n)}"))
monFD=${REPLY##*/}

eval "exec ${monFD}<>${monFifo}"

echo "pas besoin d'un chemin pour spécifier le fifo" >&${monFD}
read -u${monFD}

echo "${REPLY}"

eval "exec ${monFD}>&- ${monFD}<&-"
rm "${monFifo}"

Ha oui, je n’ai pas testé ta syntaxe. Par contre, quand je fais “read -u3”, ça ne lit rien. Ca marche pour toi ?

EDIT : J’en profite pour te demander si tu connais un moyen pour que quand on lit dans le fifo, ce qui est lu ne soit pas “consommé”, et reste dans le fifo ? Parce que sinon je suis obligé de remettre l’information dans le fifo à chaque fois, immédiatement après l’avoir lue (mais bon c’est pas si gênant).

[quote=“branch”]Ha oui, je n’ai pas testé ta syntaxe. Par contre, quand je fais “read -u3”, ça ne lit rien. Ca marche pour toi ?
[/quote]
oui, sinon je ne te l’aurais pas donné :wink:

[quote=“branch”]
EDIT : J’en profite pour te demander si tu connais un moyen pour que quand on lit dans le fifo, ce qui est lu ne soit pas “consommé”, et reste dans le fifo ? Parce que sinon je suis obligé de remettre l’information dans le fifo à chaque fois, immédiatement après l’avoir lue (mais bon c’est pas si gênant).[/quote]
non, pas moyen … c’est le principe du fifo !

EDIT : j’ai apporté un complément d’info sur mon précédant post…

Là je ne comprend pas trop. Voici la séquence d’actions que j’ai faites :

(Je calcule le prochain descripteur libre : c'est 4) mkfifo fifo4 exec 4<> fifo4 echo "abcd" >&4 read -u4

Et là, ça n’affiche rien, juste un retour chariot et le prompt. Si je refait la commande read -u4 , le shell attend indéfiniment. Ca veut dire que le fifo a bien été lu mais comment récupérer le résultat de la lecture ?

ah… je comprends.
c’est normal
read -u4 permet de lire le fd4 et de valoriser la variable REPLY.
fait un echo “${REPLY}” ensuite…