Journal Faire de la magie avec son .inputrc

Posté par  (site web personnel) . Licence CC By‑SA.
81
24
mar.
2015

Bonjour nal et naleux,

Faites-vous partie de ces barbus définissant des dizaines et des dizaines d'alias dans leur .bashrc, afin d'enlarge your productivity au maximum ? Mais savez-vous qu'il existe un fichier encore pluss trop bien, permettant de faire virevolter votre productivité à un niveau inégalé : le .inputrc ?

Celui-ci permet de définir des raccourcis claviers qui seront disponibles dans tous les clients utilisant readline (donc bash, mais pas zsh… Raison principale pour laquelle je ne suis toujours pas passé à zsh !)

En premier lieu, les utilisateurs de vi seront heureux de savoir que readline contient un mode vi qui permet d'utiliser des commandes à la vi. Pour l'activer, ajouter ceci au fichier :

set editing-mode vi
set keymap vi-command

Pour la liste des raccourcis, cf. ici

Vous pouvez aussi régler vos raccourcis clavier qui ne seraient pas reconnus par bash, notamment les flèches, qui peuvent avoir plusieurs variantes : « [1;5C », « [5C » ou encore « \e[C ». Pour savoir ce que les différentes combinaisons de touche renvoient, lancez par exemple un « sleep 10 » puis voyez quels représentations sont affichées. Pour les utiliser dans votre .inputrc, remplacez « ^[ » par « \e ». Ainsi, si par exemple les déplacement en CTRL-flèches posent problème, ajoutez ceci :

    # mappings for Ctrl-left-arrow and Ctrl-right-arrow for word moving
    "\e[1;5C": forward-word
    "\e[1;5D": backward-word
    "\e[5C": forward-word
    "\e[5D": backward-word
    "\e\e[C": forward-word
    "\e\e[D": backward-word

Un autre truc pratique : pour chercher directement dans votre historique les commandes commençant par la commande en cours avec CTRL+flèche haut ou bas, ajoutez :

"\e[1;5A": history-search-backward
"\e[1;5B": history-search-forward

Bref, passons les astuces triviales que vous trouverez un peu partout sur Internet ou en lisant le man bash, et voyons voir ce qu'on peut définir d'un peu plus intéressant. En premier lieu, il est intéressant de noter qu'on peut définir à peu près n'importe quelle séquence de touche sur un raccourci clavier. Ainsi, si je définis ceci :

"\ej": "jobs\n"

taper M-j m'affichera automatiquement les processus en arrière-plan. Notez bien le retour chariot, qui m'évite d'appuyer sur entrée. On ne dirait pas comme ça, mais ce raccourci est indispensable : l'essayer, c'est l'adopter !
On peut affiner encore un peu l'utilité du raccourci en le rendant fonctionnel également en cours d'écriture de commande :

    "\ej": "\C-a\C-kjobs\n"

Cela rajoute une séquence de touches (C-a C-k) qui coupe toute la ligne. Vous pouvez rajouter un C-y à la fin pour recoller la ligne, mais notez que ça collera de vieilles données si vous faites cela sur une ligne vide.

Attention, notez que définir un raccourci n'efface pas forcément le précédent, ce qui donne parfois des résultats bizarres (les deux commandes peuvent être exécutées). Vérifiez donc que le raccourci n'est pas déjà attribué, par exemple sur la page wikipédia anglais de bash (oui, tout l'alphabet est déjà utilisé avec CTRL).

Avant d'aller encore plus loin, voilà en vrac d'autres raccourcis très utiles :

    # Ajouter "| less" à la fin de la ligne
    "\el": "\C-e | less"
    # Remplacer le premier mot de la commande par less
    "\eL": "\C-a\eDless\C-e"
    # Remplacer le premier mot de la commande par ls
    "\eK": "\C-a\eDls\C-e"
    # Ajouter "sudo" au début de la ligne
    "\es": "\C-asudo \C-e"
    # Afficher un processus précis (place le curseur entre les quotes)
    # Attention : écrase M-p
    "\ep": '\C-a\C-kps aux | grep -i ""\C-b'
    # grep
    "\eG": '\C-a\C-kgrep -ri "" .\C-b\C-b\C-b'
    "\eg": '\C-a\C-kgrep -r "" .\C-b\C-b\C-b'
    # find (avec copie du résultat dans le clipboard, nécessite le paquet xclip)
    "\eF": '\C-a\C-kfind . -iname "**" | tee >(tr -d \'\\n\' | xclip)\C-a\ef\ef\C-f\C-f\C-f'
    # Ouvrir le man correspondant à la commande en cours
    # (remplace "commande args" par "man commande")
    "\em": '\C-aman \ef\C-k'
    # Copier une seconde fois le premier argument de la commande
    # (Ne fonctionne que pour des commandes simples)
    # Pratique lorsqu'on écrit "mv nom_de\ -\ fichier_\[\ complexe\] nom_de\ -\ fichier_\[\ complexe\].txt"
    "\er": '\C-a\ef\C-k\C-y\C-y'

Bon, voilà, on a défini plein de commandes utiles. Mais peut-on aller plus loin ? Eh bien, réjouis-toi cher journal, car c'est ce que nous allons faire !
Bash a une fonction assez exotique que, je parie, vous n'utilisez pas tous les jours : M-C-e. Celle-ci effectue une expansion de la ligne de commande en cours : alias, sous-commandes et historique. Par exemple, si ma commande est :

$ ls $(cat fichier_a_regarder)

et que fichier_a_regarder contient « toto », alors la commande devient :

$ ls toto

Vous vous demandez à quoi cela va nous servir ? Et bien, à exécuter des commandes pour nous aider à écrire notre ligne de commande, pardi !
Par exemple, si on fait :

 "\ea": ' \\\'$(ls | dmenu)\\\'\e\C-e'

Cela va nous ouvrir un dmenu listant les fichiers du dossier en cours. On choisit l'un d'eux, et ça colle son nom dans la commande en cours. Pratique quand on a un dossier rempli de fichiers aux noms tordus et similaires !

Terminons par mon raccourci favori :

    "\e:": '\\\'$(ls -t -1 -d * | head -n 1)\\\' \e\C-e'

Qu'est-ce que ça fait ? Eh bien, ça va chercher le fichier ou dossier le plus récent du dossier en cours et nous le colle dans la ligne de commande en cours. Très pratique quand on passe son temps à enregistrer des fichiers et à oublier leur nom 10 secondes plus tard.

Notez tout de même que l'expansion remplace toutes les commandes de la ligne de commande : faites donc attention à ce que vous faites, pour ne pas exécuter de commande par erreur. Notez aussi que ça remplace les alias par leur valeur, ce qui peut être ennuyeux.

Allez, en bonus, si on se déplaçait dans l'arborescence à coup de flèches, à la ranger ?
Pour ce faire, plaçons ces lignes dans le .inputrc :

    "\e[1;3D": '\C-a\C-kcd_left\n'
    "\e[1;3C": '\C-a\C-kcd_right\n'
    "\e[1;3A": '\C-a\C-kcd_up\n'
    "\e[1;3B": '\C-a\C-kcd_down\n'

Puis définissons les alias associés dans le .bashrc :

    alias cd_left='cd .. && ls'
    alias cd_right='cd "$(first_folder)" 2>/dev/null && ls'
    alias cd_up='cd "$(prev_folder)" 2>/dev/null && ls'
    alias cd_down='cd "$(next_folder)" 2>/dev/null && ls'

Et les fonctions associées (que je ne détaille pas, je laisse les trolls critiquer la piètre qualité de mon code) :

    prev_folder() {
        prev_dirs=$(ls -1 --group-directories-first ..)
        IFS=$'\n' dirs=($(ls -1 --group-directories-first ..))
        pos_dir=$(echo "$prev_dirs" | grep -n "^$(basename "$(pwd)")$" | perl -pe 's/^(\d+).*/\1/')
        pos_prev_dir=$(( $pos_dir - 2 ))
        prev_dir=../${dirs["$pos_prev_dir"]}
        if [ -d "$prev_dir" ]
        then
        echo "$prev_dir"
        else
        echo "/dev/null"
        fi
    }

    next_folder() {
        prev_dirs=$(ls -1 --group-directories-first ..)
        IFS=$'\n' dirs=($(ls -1 --group-directories-first ..))
        pos_dir=$(echo "$prev_dirs" | grep -n "^$(basename "$(pwd)")$" | perl -pe 's/^(\d+).*/\1/')
        # array start at 0, but grep starts numerotating at 1
        # Hence, there is no need to add 1
        next_dir=../${dirs["$pos_dir"]}
        if [ -d "$next_dir" -a "$next_dir" != "../" ]
        then
        echo "../${dirs["$pos_dir"]}"
        else
        echo "/dev/null"
        fi
    }

    first_folder() {
        first_dir=$(ls -1 --group-directories-first . | grep -m 1 "")
        if [ -d "$first_dir" ]
        then
        # without ./, the folder will put printed because of CDPATH
        echo "./$first_dir"
        else
        echo "/dev/null"
        fi
    }

Et finalement, empêchons ces commandes de polluer notre historique :

   export HISTIGNORE="cd_*"

Et voilà, ça marche !

J'espère que ce journal vous donnera plein d'idées géniales que vous posterez en commentaire et qui iront compléter mon .inputrc !

  • # Pfiou !

    Posté par  . Évalué à 8.

    Et beh, moi qui laisse mon .bashrc tranquille, j'ai un max de productivité qui m'attend.

    • [^] # Re: Pfiou !

      Posté par  (Mastodon) . Évalué à 5.

      Je te foutrais tout ça au Goulag moi…

      En théorie, la théorie et la pratique c'est pareil. En pratique c'est pas vrai.

      • [^] # Re: Pfiou !

        Posté par  . Évalué à 0.

        je préfère le goulash (ou goubash)

        « Le pouvoir des Tripodes dépendait de la résignation des hommes à l'esclavage. » -- John Christopher

        • [^] # Re: Pfiou !

          Posté par  . Évalué à -2.

          Les blagues… NE S’EXPLIQUENT PAS !

  • # Raccourcis souris

    Posté par  . Évalué à -10.

    J'ai un super-truc à partager : au lieu de faire Ctrl-Shift-A Alt-Z Ctrl-espace pour sauver un fichier, il existe certains logiciels où on peut faire un raccourcis souris : on clique sur "Fichier", puis sur "Sauvegarder". C'est une question d'habitude, mais on arrive à passer quelques semaines sans tendinites.

    • [^] # Re: Raccourcis souris

      Posté par  . Évalué à 1.

      Pas mal ! Mais j'ai une petite préférence pour les raccourcis trackball ;)

    • [^] # Re: Raccourcis souris

      Posté par  (Mastodon) . Évalué à 9.

      CTRL+S ! Mais sur ton commentaire, on aurait préféré faire CTRL+Z

    • [^] # Re: Raccourcis souris

      Posté par  (site web personnel) . Évalué à 8.

      Mais sauvegarder c'est « has been » ! Sur les IDE Jetbrain (Intellij, Webstorm, Pycharm, RubyMine, etc) : plus besoin de sauvegarder, c'est tout automatique et incrémentale.

      Et à vrai dire : c'est juste bluffant !

  • # bip bip bip

    Posté par  (site web personnel) . Évalué à 10.

    La commande la plus utile de l'inputrc reste tout de même le set bell-style none pour éviter les bips intempestifs.

  • # Insensibilité à la casse pour la complétion des noms de fichiers

    Posté par  . Évalué à 10.

    Pour moi, la modif indispensable dans le .inputrc concerne la complétion, sensible à la casse par défaut. Pour y remédier :
    set completion-ignore-case on

  • # Pour classer vos raccourcis par applications

    Posté par  . Évalué à 10.

    Il est possible de restreindre vos raccourcis pour une application donnée. Je vous laisse quelques exemples.

    $if Bash
        # Edit the path
        "\C-xp": "PATH=${PATH}\e\C-e\C-a\ef\C-f"
        # Insert open and close double quotes
        "\C-x\"": "\"\"\C-b"
        # Insert a backslash (testing backslash escapes in sequences and macros)
        "\C-x\\": "\\"
        # Quote the current or previous word
        "\C-xq": "\eb\"\ef\""
        # Add a binding to refresh the line, which is unbound
        "\C-xr": redraw-current-line
        # Edit variable on current line.
        "\M-\C-v": "\C-a\C-k$\C-y\M-\C-e\C-a\C-y="
        # Non-incremental style of history completion
        "\ep": history-search-backward
        "\en": history-search-forward
    $endif
    $if Python
        # http://albatross.dnsdojo.net/apache2-default/wiki/index.php/Python_Readline_Completions
        ...
    $endif
    $if Mysql
        "\C-xc": "SELECT count(*) FROM "
        "\C-d": "desc "
        "\C-l": "show tables;\n"
    $endif
    $if Lftp
        "\C-g": "lftp   "
    $endif
    $if Ftp
        "\C-xg": "get \M-?"
        "\C-xt": "put \M-?"
        "\M-.": yank-last-arg
    $endif

    Vous pouvez également le faire par mode:

        $if mode=vi
           ...
        $endif
    • [^] # Re: Pour classer vos raccourcis par applications

      Posté par  (Mastodon) . Évalué à 6.

      Pas mal du tout, mais je ne comprends pas comment ça marche.
      Ces raccourcis ne sont valables que quand on exécute une certaine application ? Comment est déterminé ce nom ? (le $if Toto) ?

      Merci d'avance pour les détails

      En théorie, la théorie et la pratique c'est pareil. En pratique c'est pas vrai.

  • # Zsh

    Posté par  . Évalué à 10.

    donc bash, mais pas zsh… Raison principale pour laquelle je ne suis toujours pas passé à zsh !

    Et si tu préfère c'est très bien. Moi tu m'as donné de bonnes idées, il faudra que je regarde comment les mettre en place avec zle (le readline de zsh) :-)

    Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: Zsh

      Posté par  (site web personnel) . Évalué à 4.

      N'hésite pas à les partager ! Voire à faire un journal spécial zsh…

    • [^] # Re: Zsh

      Posté par  . Évalué à 5.

      # Remplacer des touches par d'autres touches
      bindkey -s '\el' '^e | less'
      # lancer une commande ZLE
      bindkey "\e[1;5C" forward-word

      Cependant, ce n'est pas forcément la meilleure façon de le faire. Par exemple, le journal utilise :

       "\em": '\C-aman \ef\C-k'

      alors que zsh propose déjà le raccourci Meta-H qui affiche le manuel de la commande courante, tout en restaurant correctement la commande qui était en cours d'édition à la fin.
      En fait, le raccourci Meta-H est affectée à une petite fonction dont on peut voir l'implémentation aisément :

      % bindkey -L '^[H'    
      bindkey "^[H" run-help
      % typeset -pf run-help | pastebinit
      http://paste.debian.net/163263/
      • [^] # Re: Zsh

        Posté par  . Évalué à 5.

        Oui je zle possède de base (pour ce que j'utilise) :

        • Echap + q : supprime la ligne courante le temps de lancer une commande puis la recolle (si on a oublié le nom de quelque chose on peut lancer une commande puis reprendre sa ligne ou si on a oublié de créer un dossier)
        • Echap + ? : lance la commande which-command sur la commande courante puis redonne la ligne dans l'état précédent (prend correctement en charge les choses comme PATH=tutu ls /tmp)
        # Remplacer des touches par d'autres touches
        bindkey -s '\el' '^e | less'

        Perso ça je le fais avec :

        typeset -Ag abbreviations
        abbreviations=(
          'Ia'    '| awk'
          'Ig'    '| grep'
          'Ip'    "| $PAGER"
          'Ih'    '| head'
          'It'    '| tail'
          'Is'    '| sort'
          'Iw'    '| wc'
        )

        Ce qui permet de le faire avec "Ip ".

        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

      • [^] # Re: Zsh

        Posté par  (site web personnel) . Évalué à 1.

        Hum, ça donne vraiment envie de passer à zsh tout ça…

  • # Modifier readline via .bashrc

    Posté par  . Évalué à 4.

    On peut aussi tout faire dans le .bashrc, question d'éviter de s'emmerder avec encore un autre fichier en utilisant la command "bind"…

    J'en profite pour poster ma partie, avec notamment des trucs sur l'autocompletion (pas de hidden files sans ., cacher le préfixe commun entre plusieurs fichiers, …)

    # readline settings
    # ignore case for auto completion
    bind "set completion-ignore-case on"
    # ignore diff between - and _
    bind "set completion-map-case on"
    # show directly completion results after first TAB
    bind "set show-all-if-unmodified on"
    # limit what is shown for completion (hide prefix bigger than 2 char)
    bind "set completion-prefix-display-length 2"
    # do not complete hidden files
    bind "set match-hidden-files off"
    
  • # ok, corrigeons. :p

    Posté par  (site web personnel) . Évalué à 10. Dernière modification le 28 mars 2015 à 21:50.

    C'est pour troller parce que ca fait plaisir. Et c'est pour eduquer parce que bon, le shell ca n'a pas non plus a etre crado tout le temps, surtout quand on veut donner du code.

    La commande ls est prevue pour un usage interactif, pas pour etre parsee. Son output n'est pas fiable. De plus utiliser le resultat d'expansion de commandes comme noms de fichier c'est plutot hasardeux et souvent inutile… De plus, si tu ne declares pas tes variables de fonction "local", tu pourris l'environnement de l'appelant, fais attention.

    En built-in, tu peux faire plus rapide et plus secure comme ca :

    cd_up() {
     local opt_saving=$(shopt -p failglob nullglob)
     shopt -s nullglob
     shopt -u failglob
     local -a dirs=( */ )
     eval "$opt_saving"
     [[ ${#dirs[@]} -ne 0 ]] || return 0
     cd "${dirs[0]}" 2>/dev/null && ls
    }
  • # Utilisation du mode vi ?

    Posté par  . Évalué à 5. Dernière modification le 28 mars 2015 à 21:50.

    En premier lieu, les utilisateurs de vi seront heureux de savoir que readline contient un mode vi qui permet d'utiliser des commandes à la vi.

    Bien que vimiste convaincu, je n'ai jamais réussi à m'y faire sur la ligne de commande. C'est trop chiant, pour moi on passe tellement de temps en mode insertion que passer à autre chose devient chiant. Et vous, comment vous l'utilisez ?

    donc bash, mais pas zsh…

    Ça s'adapte facilement, vu que les raccourcis sont à peu près identiques. Exemple avec des Alt+qqch :

    bindkey \\eC backward-word
    bindkey \\eT down-line-or-history
    bindkey \\eS up-line-or-history
    bindkey \\eR forward-word
    • [^] # Re: Utilisation du mode vi ?

      Posté par  (site web personnel) . Évalué à 2.

      Bonne nouvelle, il faudra que je regarde si ça s'adapte facilement pour pouvoir passer à zsh… Je suis particulièrement jaloux de l'opérateur ** de zsh, qui me rendrait bien des services !

  • # Zsh

    Posté par  (site web personnel) . Évalué à 4. Dernière modification le 26 mars 2015 à 21:44.

    Qu'est-ce que ça fait ? Eh bien, ça va chercher le fichier ou dossier le plus récent du dossier en cours et nous le colle dans la ligne de commande en cours. Très pratique quand on passe son temps à enregistrer des fichiers et à oublier leur nom 10 secondes plus tard.

    Comme ^Xm dans zsh, quoi…

  • # Ma calculatrice préférée :-)

    Posté par  (site web personnel) . Évalué à 1.

    Depuis de nombreuses années, plutôt que d’utiliser gnome-calc, ou qalculate, etc. ma calculatrice préférée est ce petit script que j’ai mis dans mon PATH:

    #!/bin/bash
    exec xterm -T Calculatrice -geometry 35x25 -fg lightblue -bg black -e bash -c \
      'echo CALCULATRICE; export INPUTRC=~/.inputrc.calculatrice; exec bc -liq <(echo "tva=1.196")'

    La partie <(echo "tva=1.196") est bien sûr optionnelle ; elle me servait à avoir d’emblée une variable tva pour pouvoir par exemple demander 15*tva pour obtenir un prix TTC à partir d’un prix de départ HT.

    Et dans le fichier ~/.inputrc.calculatrice, je mets le contenu suivant :

    set disable-completion on
    set echo-control-characters off
    ",": "."
    " ": ""
    "\xA0": ""
    

    Cette calculatrice a de nombreux atouts :
    — On peut copier-coller des valeurs depuis d’autres applications et ça efface automatiquement les espaces sécable ou insécables parasites, ainsi que les tabulations.
    — Lors des copier-coller ou en cours de saisie, la virgule est automatiquement remplacée par le point décimal qu’exige bc : on peut donc utiliser indifféremment les deux.
    — Les calculs sont historisés et éditables grâce à readline.
    — Et comme c’est bc à la base, on a du calcul en précision arbitraire (variable scale), le choix de la base en entrée (ibase) et en affichage (obase), des variables (des « mémoires ») à volonté et quelques fonction mathématiques.

    Yves.

    • [^] # Re: Ma calculatrice préférée :-)

      Posté par  (site web personnel) . Évalué à 2. Dernière modification le 03 avril 2015 à 02:08.

      La partie <(echo "tva=1.196") est bien sûr optionnelle ; elle me servait à avoir d’emblée une variable tva pour pouvoir par exemple demander 15*tva pour obtenir un prix TTC à partir d’un prix de départ HT.

      Ouais alors soit c'est un truc de kéké dont tu n'as absolument pas besoin (parce que tu n'as pas à gérer de TVA), soit t'es sacrément dans la merde et tu devrais te préparer pour ton redressement, vu que ça fait plus d'un an qu'elle est passé à 20% (enfin le taux normal ; le fait que tu oublies les taux intermédiaire, réduit et particulier tendent vers la première explication).

      • [^] # Re: Ma calculatrice préférée :-)

        Posté par  (site web personnel) . Évalué à 2.

        Mauvaise réponse, essaie encore :-p Note l’utilisation du passé dans « elle me servait ».

        Je suis ce qu’on appelle un prestataire ; autrement dit, je vais de mission en mission. J’ai eu besoin d’utiliser quotidiennement ce taux de TVA de 19,6% dans mes calculs au cours d’une mission.

        Aujourd’hui, je n’ai plus besoin de cette variable ; je ne l’utilise plus. Je ne connais pas sa nouvelle valeur. Ça ne m’intéresse pas ; si ça se trouve, ça va encore changer… Mais le script est resté en l’état.

        J’ai failli supprimer cette partie, mais je l’ai laissée pour montrer qu’on pouvait injecter des variables couramment utilisées, ce qui est somme toute la fonctionnalité intéressante.

  • # Mapping clavier dans vi

    Posté par  . Évalué à 1.

    Je rebondis sur un sujet connexe car j'ai eu beau chercher, je n'ai toujours pas réussi à trouver comment faire : est-ce que quelqu'un saurait comment conserver les emplacements des raccourcis clavier dans vi lorsqu'on utilise un clavier français plutôt qu'un clavier américain ?

    En effet les raccourcis clavier de vi ont été pensés et optimisés pour une disposition QWERTY, du coup quand on est en AZERTY ça devient assez compliqué…

    Merci à celles et ceux qui sauront me répondre.

    • [^] # Re: Mapping clavier dans vi

      Posté par  . Évalué à 2.

      Il faudrait remapper les touches qui te posent problème, tu aurais alors un vimrc que tu activerais quand tu es en azery et un autre (sans remappage) pour le qwery.

      Un autre moyen plus radical mais plus efficace : utilise, comme je l'ai fait, un des excellents logiciels qui t'apprennent à taper en aveugle (klavaro, par exemple, ça a été mon choix) et tu ne chercheras plus jamais où sont les touches spéciales quand tu changeras d'ordi et de locale, il suffira de mettre le clavier en mode qwerty du pays que tu éliras comme ton préféré. Ou alors bepo, mais je doute que ça résolve ton problème avec vim ;-). Franchement, ça demande un peu de travail au début, mais après 15j ou 3 semaines avec 20mn par jour, tu es opérationnel et bien plus efficace dans la frappe.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.