bash_history: gefährliche Befehle auskommentieren: `#`

bash_history: gefährliche Befehle auskommentieren: `#`

Um zu verhindern, dass „gefährliche“ Befehle im Bash-Verlauf protokolliert werden, habe ich meiner .bashrcDatei die folgende Zeile hinzugefügt:

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

das funktioniert gut, hat aber einen Nebeneffekt: Ich kann nicht den gesamten Verlauf der auf einer Maschine ausgeführten Befehle sehen. Angenommen, ich habe mehrere Maschinen für Experimente und möchte alle ausgeführten Befehle sehen können. Ich würde die interne Bash verwenden, historyum ausgeführte Befehle anzuzeigen, und vielleicht grep für das heutige Datum verwenden:

history | grep Sep-28

Ich möchte, dass auch „gefährliche“ Befehle protokolliert werden, aber ein #an den Anfang der Zeile gesetzt wird, sodass kein Schaden entsteht, wenn ich den Befehl versehentlich aus dem Verlauf ausführe.

Ich habe keine Ahnung, ob das möglich ist.

Aktualisierung und Klarstellung:

Der Hauptgrund, warum dies für mich ein Problem darstellt, ist, dass ich normalerweise von mehreren Terminals aus mit meinem Computer verbunden bin und jeder auf einem Terminal ausgeführte Befehl sofort in den Verlauf anderer Terminals eingelesen wird. Dies wird erreicht durch

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

Stellen wir uns vor, ich habe zwei Terminals geöffnet. In einem cat /dev/foo > file.outläuft ein Prozess. Im zweiten überprüfe ich den Fortschritt mit ls -lAhF. Ich wiederhole dies immer wieder, lsindem ich Upund drücke ENTER(das heißt, den letzten Befehl aus dem Verlauf). Sobald der erste Befehl abgeschlossen ist, ist der letzte Befehl aus dem Verlauf nicht mehr ls, sondern cat /dev/foo > file.out. Wenn ich nicht aufpasse, starte ich cat erneut und überschreibe file.out.

Ich möchte erreichen, dass dem Befehl cat ein vorangestellt wird #, damit er nicht ausgeführt wird. Ich würde ihn jedoch weiterhin im Verlauf sehen und ihn (wenn es sich um einen langen Befehl handelt) wiederverwenden können, indem ich die Kommentierung aufhebe.

Antwort1

Ich werde Ihre Frage nicht genau beantworten, Ihnen aber vielleicht eine alternative Lösung für Ihr Problem anbieten.

Wenn ich Sie richtig verstehe, sind Sie besorgt über mögliche Tippfehler, zum Beispiel !rmwenn der vorherige rmBefehl im Verlauf etwas entfernt, das Sie behalten möchten.

In diesem Fall ein schönesSchlagOption ist histverify. Wenn Sie shopt -s histverify, und wenn Sie einen Befehl mit dem Ausrufezeichen zurückrufen !, wird dieser nicht sofort ausgeführt, sondern in die Readline geladen, so dass Sie entscheiden können, ob Sie ihn ausführen möchten oder nicht. Dies gibt Ihnen auch die Möglichkeit, ihn zu bearbeiten.

Versuch es:

  • Ohne 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
    
  • Mit histverify:

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

    In diesem Fall befindet sich der Cursor am Ende der Zeile und ist bereit, sie erneut zu starten (oder nicht) oder sie zu bearbeiten.

Wenn Ihnen diese Option gefällt, fügen Sie sie in Ihr .bashrc: ein.

shopt -s histverify

Antwort2

Sie könnten beispielsweise Folgendes tun:

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

Die Idee dahinter ist, dass wir vor jeder Eingabeaufforderung den letzten Verlaufseintrag ( history 1) überprüfen und ob es einer dergefährlichneuen, löschen wir es ( history -d) und fügen es mit einem wieder #hinzu history -s.

(natürlich müssen Sie Ihre Einstellung entfernen HISTIGNORE).

Ein unerwünschter Nebeneffekt davon ist jedoch, dass es dieGeschichte Zeitdavon rm... mvBefehle.

Um das Problem zu beheben, könnte folgende Alternative möglich sein:

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

Dieses Mal zeichnen wir die Zeit des letzten Verlaufs auf und verwenden zum erneuten Hinzufügen der Verlaufszeile history -reine temporäre Datei (das hier vorliegende Dokument), die den Zeitstempel enthält.

Sie möchten, dass die fixhistvor Ihrer ausgeführt wird history -a; history -c; history -r. Leider bashhat die aktuelle Version von einen Fehler in , der history -adie von uns hinzugefügte zusätzliche Zeile nicht speichert. Eine Umgehungslösung besteht darin, sie stattdessen zu schreiben:

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

Das heißt, wir hängen den kommentierten Befehl selbst an die HISTFILE an, statt history -aes einfach machen zu lassen.

Antwort3

Derakzeptierte Antwort von Stéphaneist absolut erstaunlich. Unten ist meine Übernahme davon, bei der die Musterübereinstimmung etwas anders erfolgt.

Die Verwendung ifvon -Anweisungen anstelle von caseermöglicht mir die Verwendung von Regex-Matching ( [[ "$cmd" =~ <REGEX> ]]), der folgende Code funktioniert nach der folgenden Logik:

  • In folgenden Fällen nichts zum Verlauf hinzufügen:
    • Die Befehlszeile beginnt oder endet mit zwei oder mehr aufeinanderfolgenden Leerzeichen.
      Beispiel:
      • <spc><spc>history
  • Im folgenden Fall einen kommentierten Befehl hinzufügen:
    • rmoder mvBefehle mit CLI-Schaltern
    • Die Befehlszeile beginnt mit :;oder einem einzelnen Leerzeichen oder endet mit ;:
      Beispielen:
    • rm -R /tmp/somedir

    • echo "doing something dangerous I don't want to trip by accident";:

      Auch hier gibt es Einschränkungen, mit denen ich einverstanden bin, zum Beispiel:

    • echo ";rm -something"

  • Andernfalls zum Verlauf hinzufügen
    . Beispiele:
    • rm somefile
    • echo ";rm -something"

Der Code

_history_hook() {
# The code below is from https://unix.stackexchange.com/a/110056/138012
# history builtiun options used:  https://www.gnu.org/software/bash/manual/html_node/Bash-History-Builtins.html
# history -a : Append new (mem) lines to history (file)
# history -d : Delete range (mem)
# history -c : Clear history list (mem) ++
# history -r : Read (file) append to (mem) ++
# ++ needed to circumvent a bug issue.
   local cmd time histnum
   cmd=$(HISTTIMEFORMAT='<%s>' history 1)
   histnum=$((${cmd%%[<*]*}))
   time="${cmd%%>*}"
   time=${time#*<}
   cmd="${cmd#*>}"
   # different behaviors for different patterns
   if [[ "$cmd" =~ ^\ \ +.*|\ +\ $ ]]; then
   # delete only (two spaces - leading or trailing)
       history -d "$histnum" # delete
   elif [[ "$cmd" =~ ((^|;[[:space:]]*)(rm|mv)\ +-)|^\ |^:\;|\;:$ ]]; then
   # rewrite history as a 'safe version'
       history -d "$histnum" # delete
       history -a
       [ -f "$HISTFILE" ] && printf '# %s\n' "$time" "$cmd" >> "$HISTFILE"
   else
   # append 'as usual'
       history -a
   fi
   # rewriting history file
   history -c
   history -r
}

export HISTCONTROL=erasedups # This does not seem to work right now - need to fix this
export PROMPT_COMMAND="$PROMPT_COMMAND; _history_hook"

verwandte Informationen