Comment inclure des commandes dans la PS1 de Bash sans casser le calcul de la longueur de ligne?

13

Tonin a signalé un bogue dans mon invite par défaut . Exemple minimal:

  1. Définir PS1:

    PS1='$(exit_code=$?; [[ $exit_code -eq 0 ]] || printf %s $(tput setaf 1) $exit_code $(tput sgr0) " ")$ '

    À ce stade, l'invite ressemble à ceci:

    $ 
  2. Déclenchez maintenant la sortie du code de sortie en exécutant:

    false

    Maintenant, l'invite contient le code de sortie en rouge au début de la ligne:

    1 $ 
  3. Appuyez sur Ctrl- r.
  4. Tapez "false". Maintenant, l'invite contient uniquement la recherche:

    (reverse-i-search)`false': false
  5. Appuyez sur Enter.

L'historique du terminal résultant contient désormais les éléments suivants:

1 $ch)`false': false

Production attendue:

1 $ false

Autrement dit, il semble que la sortie de la recherche d'historique soit mélangée à l'invite et masque la commande réelle qui a été exécutée.

J'ai essayé de contourner cela en utilisantPROMPT_COMMAND :

set_exit_code() {
    exit_code=$?
    [[ $exit_code -eq 0 ]] || printf %s $(tput setaf 1) $exit_code $(tput sgr0) " "
}
set_bash_prompt() {
    PS1='$(set_exit_code)$ ' # Double quotes give the same result
}
PROMPT_COMMAND=set_bash_prompt

Cela ne semble pas fonctionner - la ligne est exactement la même qu'avant après la recherche et l'exécution.

Comment puis-je réparer cela?

l0b0
la source
1
Cela semble être la continuation d' unix.stackexchange.com
71012

Réponses:

8

J'ai trouvé la réponse sur askubuntu.com . @qeirha a mentionné que vous devez dire à bash que la séquence de caractères ne doit pas être comptée dans la longueur de l'invite, et vous le faites en l'enfermant dans \[ \]. Sur la base de l'exemple fourni, voici une solution:

red=$(tput setaf 1)

reset=$(tput sgr0)

[ "$PS1" = "\\s-\\v\\\$ " ] && PS1='$(exit_code=$?; [[ $exit_code -eq 0 ]] || printf %s \[$red\] $exit_code \[$reset\] " ")$ '
Timothy Martin
la source
Pas besoin d'aller demander à Ubuntu . Nous avons déjà suffisamment de réponses à cette question ici aussi.
manatwork
Merci pour les conseils @manatwork! Je voulais donner un crédit approprié pour l'explication et fourni la référence à titre de courtoisie.
Timothy Martin
Donner du crédit n'est pas un problème. Mais en parlant de problème: les barres obliques inversées ont disparu de Markdown, donc votre \ \ est devenu [dans votre message, donc le code affiché n'était pas fonctionnel en le copiant-collant dans le terminal. Cela peut être évité en utilisant du code en ligne ou un balisage de bloc de code. ( Comment formater mes articles en utilisant Markdown ou HTML? )
Manatwork
1
Oh! J'ai déjà résolu le même problème pour un autre PS1code, pourquoi ne l'ai-je pas vu?
l0b0
1
PS1='$(exit_code=$?; [[ $exit_code -eq 0 ]] || printf %s \[$(tput setaf 1)\] $exit_code \[$(tput sgr0)\] " ")$ '

(Désolé, aucune explication ici. Voir Comment personnaliser correctement la PS1? Ou toute autre question sur les problèmes de calcul de la longueur des invites et \[... \])

2 tours
la source
À la deuxième question @ l0b0, j'ajouterai que l'utilisation de PS1 et \[...\]fonctionne très bien tant que vous pouvez mettre tout le code que vous souhaitez générer votre invite dans une seule chaîne. Cependant, si vous souhaitez diviser votre code en petites fonctions, vous arrivez à un point où vous ne pouvez pas placer les crochets de début et de fin dans la même chaîne / fonction. Et cela rompt le retour à la ligne. À moins que vous ne recouriez à l'utilisation PROMPT_COMMANDpour recalculer votre PS1à chaque invite.
Tonin
1

En développant la réponse @manatwork mais en gardant votre code divisant le PS1calcul en différentes fonctions, vous pouvez écrire votre invite de la manière suivante:

set_exit_code() {
    exit_code=$?
    [[ $exit_code -eq 0 ]] || printf "\[$(tput setaf 1)\] $exit_code \[$(tput sgr0)\] "
}
set_bash_prompt() {
    PS1="$(set_exit_code)$ " # with double quotes!
}
PROMPT_COMMAND=set_bash_prompt

Les guillemets doubles sont obligatoires lors de la définition PS1et lors de l'utilisation printfde la fonction.

Tonin
la source
Pour référence future, utilisez une fonction bash dans votre .bashrc- ne mettez pas de code dans un fichier séparé et appelez cela.
starbeamrainbowlabs