bash_history: commentez les commandes dangereuses: `#`

16

Pour empêcher la journalisation des commandes "dangereuses" dans l'historique bash, j'ai ajouté la ligne suivante à mon .bashrcfichier:

HISTIGNORE='rm *:mv *:cp *:cat*>*:pv*>*'

cela fonctionne bien, mais cela a un effet secondaire: je ne peux pas voir l'historique complet des commandes exécutées sur une machine. Disons que j'ai plusieurs machines pour les expériences, et je veux pouvoir voir toutes les commandes exécutées. J'utiliserais le bash internal historypour afficher les commandes exécutées, et peut-être grep pour la date d'aujourd'hui:

history | grep Sep-28

Ce que j'aimerais avoir, c'est aussi enregistrer les commandes "dangereuses", mais mettre un #au début de la ligne, de sorte que si j'exécute la commande à partir de l'historique par erreur, aucun dommage ne soit fait.

Je n'ai aucune idée si c'est possible.

Mise à jour et clarification:

La principale raison pour laquelle cela pose problème est que je suis généralement connecté à ma machine à partir de plusieurs terminaux, et toute commande exécutée sur un terminal est immédiatement lue dans l'historique des autres terminaux. Ceci est réalisé par

PROMPT_COMMAND="history -a; history -c; history -r"

Imaginons que j'ai deux terminaux ouverts. Dans l'un, j'ai un cat /dev/foo > file.outprocessus en cours. Dans la seconde, je vérifie la progression avec ls -lAhF. Je continue de répéter lsen appuyant sur Upet ENTER(c'est-à-dire, la dernière commande de l'historique). Dès que la première commande se termine, la dernière commande de l'historique n'est plus ls, mais cat /dev/foo > file.out. Si je ne fais pas attention, je vais redémarrer cat et écraser file.out.

Ce que j'aimerais réaliser, c'est que la commande cat serait précédée d'un #, afin qu'elle ne soit pas exécutée. Cependant, je le verrais toujours dans l'histoire et je peux le réutiliser (s'il s'agit d'une commande longue) en le décommentant.

Martin Vegter
la source
Au lieu de répéter votre commande manuellement, vous pouvez utiliser watch ls -lAhFou while sleep 1; do ls -lAhf; done; au lieu de regarder la taille du fichier, vous pouvez utiliser pv /dev/foo > file.out( ivarch.com/programs/pv.shtml ).
deltab

Réponses:

9

Vous pourriez faire quelque chose comme:

fixhist() {
   local cmd histnum
   cmd=$(HISTTIMEFORMAT=/ history 1)
   histnum=$((${cmd%%[*/]*}))
   cmd=${cmd#*/} # remove the histnum
   case $cmd in
     (rm\ *|mv\ *|...)
       history -d "$histnum" # delete
       history -s "#$cmd"    # add back with a #
   esac
}
PROMPT_COMMAND=fixhist

L'idée étant qu'avant chaque invite, nous vérifions la dernière entrée d'historique ( history 1) et si c'est l'une des plus dangereuses , nous la supprimons ( history -d) et la rajoutons avec un #avec history -s.

(évidemment, vous devez supprimer votre HISTIGNOREparamètre).

Un effet secondaire indésirable de cela est qu'il modifie la temps de l' histoire de ceux rm, mv... commandes.

Pour résoudre ce problème, une alternative pourrait être:

fixhist() {
   local cmd time histnum
   cmd=$(HISTTIMEFORMAT='<%s>' history 1)
   histnum=$((${cmd%%[<*]*}))
   time=${cmd%%>*}
   time=${time#*<}
   cmd=${cmd#*>}
   case $cmd in
     (rm\ *|mv\ *|...)
       history -d "$histnum" # delete
       HISTFILE=/dev/stdin history -r <<EOF
#$time
#$cmd
EOF
   esac
}
PROMPT_COMMAND=fixhist

Cette fois, nous enregistrons l'heure de la dernière histoire, et pour rajouter la ligne d'historique, nous utilisons à history -rpartir d'un fichier temporaire (le document ici) qui inclut l'horodatage.

Vous voudriez que cela fixhistsoit fait avant votre history -a; history -c; history -r. Malheureusement, la version actuelle de bashcontient un bogue qui history -ane sauvegarde pas la ligne supplémentaire que nous avons ajoutée. Une solution consiste à l'écrire à la place:

fixhist() {
   local cmd time histnum
   cmd=$(HISTTIMEFORMAT='<%s>' history 1)
   histnum=$((${cmd%%[<*]*}))
   time=${cmd%%>*}
   time=${time#*<}
   cmd=${cmd#*>}
   case $cmd in
     (rm\ *|mv\ *|...)
       history -d "$histnum" # delete
       history -a
       [ -f "$HISTFILE" ] && printf '#%s\n' "$time" "$cmd" >> "$HISTFILE";;
     (*)
       history -a
   esac
   history -c
   history -r
}
PROMPT_COMMAND=fixhist

C'est d'ajouter la commande commentée au HISTFILE nous-mêmes au lieu de le laisser history -afaire.

Stéphane Chazelas
la source
pouvez-vous s'il vous plaît expliquer ce que cmd=${cmd#*[0-9] }fait? Et où dois-je mettre fixhistmon PROMPT_COMMAND? Pour le moment, il contient déjàPROMPT_COMMAND="history -a; history -c; history -r"
Martin Vegter
quel est le rôle de HISTTIMEFORMAT=/? J'ai déjà mis export HISTTIMEFORMAT="%b-%d %H:%M ". Soit dit en passant, lorsque j'ajoute votre code dans mon $HOME/.bashrc, rien ne se passe.
Martin Vegter
HISTTIMEFORMAT=/est uniquement pour cette invocation de historypour obtenir une sortie comme 123 /rm xoù il est plus facile d'extraire la cmd. Il y avait un problème avec certaines versions d' bash$HISTCMDétait faux dans ce contexte, essayez la nouvelle version.
Stéphane Chazelas
ne fonctionne toujours pas. Je me demande, #le code ne fait-il pas office de commentaires .bashrc?
Martin Vegter
1
@MartinVegter, oui, pour les entrées d'historique modifiées , bashinsère un *après le numéro d'historique qui n'était pas attendu. Devrait être corrigé maintenant.
Stéphane Chazelas
11

Je ne vais pas répondre exactement à votre question, mais peut-être vous donner une solution alternative à votre problème.

Si je comprends bien, vous vous inquiétez des erreurs que vous pourriez commettre en tapant, par exemple, !rms'il arrivait que la rmcommande précédente de l'historique supprime quelque chose que vous aimeriez conserver.

Dans ce cas, une belle option bash est histverify. Si vous shopt -s histverify, et si vous vous souvenez d'une commande avec le bang! , elle ne s'exécutera pas immédiatement, mais sera chargée dans la ligne de lecture afin que vous puissiez décider de l'exécuter ou non, et cela vous donne également la possibilité de la modifier.

Essayez-le:

  • Sans histverify:

    $ touch some_foo
    $ rm some_foo
    $ touch some_foo
    $ !rm
    rm some_foo
    $ # oooops in fact I'd've like to keep it this time
  • Avec histverify:

    $ shopt -s histverify
    $ touch some_foo
    $ rm some_foo
    $ touch some_foo
    $ !rm
    $ rm some_foo <cursor here>

    Dans ce cas, vous aurez le curseur à la fin de la ligne, prêt à le relancer - ou non - ou à le modifier.

Si vous aimez cette option, mettez-la dans votre .bashrc:

shopt -s histverify
gniourf_gniourf
la source