bash_history: gefährliche Befehle auskommentieren: `#`

16

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

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

Das funktioniert gut, hat aber einen Nebeneffekt: Ich kann den vollständigen Verlauf der auf einem Computer ausgeführten Befehle nicht 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 history, um ausgeführte Befehle anzuzeigen und vielleicht nach dem heutigen Datum zu fragen:

history | grep Sep-28

Was ich tun möchte, ist, auch "gefährliche" Befehle zu protokollieren, aber ein #an den Anfang der Zeile zu setzen, so dass, wenn ich den Befehl aus der Vergangenheit versehentlich ausführen würde, kein Schaden angerichtet würde.

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

Update und Klarstellung:

Der Hauptgrund, warum dies für mich ein Problem ist, ist, dass ich normalerweise von mehreren Terminals aus mit meinem Computer verbunden bin und jeder Befehl, der auf einem Terminal ausgeführt wird, sofort in die Historie 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 habe ich welchecat /dev/foo > file.out laufenden Prozess. Im zweiten überprüfe ich den Fortschritt mit ls -lAhF. Ich halte Wiederholung lsdurch Drücken Upund ENTER(die, letzter Befehl aus der Geschichte ist). Sobald der erste Befehl beendet ist, ist der letzte Befehl aus dem Verlauf nicht mehr vorhanden ls, aber cat /dev/foo > file.out. Wenn ich nicht aufpasse, starte ich cat erneut und überschreibe file.out.

Was ich erreichen möchte, ist, dass dem cat-Befehl ein vorangestellt wird #, damit er nicht ausgeführt wird. Ich würde es jedoch immer noch in der Geschichte sehen und kann es wiederverwenden (wenn es ein langer Befehl ist), indem ich es nicht kommentiere.

Martin Vegter
quelle
Anstatt den Befehl manuell zu wiederholen, können Sie watch ls -lAhFoder verwenden while sleep 1; do ls -lAhf; done. Anstatt die Dateigröße zu beobachten, können Sie pv /dev/foo > file.out( ivarch.com/programs/pv.shtml ) verwenden.
DeltaB

Antworten:

9

Sie könnten etwas tun wie:

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 ist, dass wir vor jeder Eingabeaufforderung den letzten Verlaufseintrag überprüfen ( history 1) und, wenn es einer der gefährlichen ist, ihn löschen ( history -d) und ihn mit einem #mit wieder hinzufügenhistory -s .

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

Ein unerwünschter Nebeneffekt davon ist jedoch, dass es die Geschichtszeit von jenen verändert rm,mv ... Befehle .

Um das zu beheben, könnte eine Alternative 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 um die Verlaufszeile wieder hinzuzufügen, verwenden wir history -r eine temporäre Datei (das Dokument hier), die den Zeitstempel enthält.

Sie möchten fixhist, dass das Lied vor Ihrem aufgeführt wird history -a; history -c; history -r. Leider hat die aktuelle Version von basheinen Fehler, history -ader die zusätzliche Zeile, die wir hinzugefügt haben , nicht speichert. Eine Abhilfe besteht darin, es 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 fügen den kommentierten Befehl an die HISTFILE an, anstatt ihn ausführen zu lassen history -a.

Stéphane Chazelas
quelle
Kannst du bitte erklären, was cmd=${cmd#*[0-9] }tut? Und wo genau soll ich fixhistmeine einfügen PROMPT_COMMAND? Im Moment enthält es bereitsPROMPT_COMMAND="history -a; history -c; history -r"
Martin Vegter
Was ist die Rolle von HISTTIMEFORMAT=/? Ich habe bereits festgelegt export HISTTIMEFORMAT="%b-%d %H:%M ". Übrigens $HOME/.bashrcpassiert nichts , wenn ich Ihren Code zu meinem hinzufüge .
Martin Vegter
HISTTIMEFORMAT=/ist nur für diesen einen Aufruf von history, um eine Ausgabe wie zu erhalten, 123 /rm xin der es einfacher ist, das cmd zu extrahieren. Es gab ein Problem mit einigen Versionen von bashwo $HISTCMDwar unecht in diesem Zusammenhang versucht die neue Version.
Stéphane Chazelas
funktioniert immer noch nicht. Ich frage mich, ob der #im Code nicht als Kommentar fungiert .bashrc?
Martin Vegter
1
@MartinVegter, ja, fügt bei geänderten Verlaufseinträgen bashein *nach der nicht erwarteten Verlaufsnummer ein. Sollte jetzt behoben sein.
Stéphane Chazelas
11

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

Wenn ich richtig verstehe, sind Sie besorgt über Fehler, die Sie durch Tippen machen könnten, zB !rmwenn es passiert ist, dass der vorherigerm Befehl im Verlauf etwas entfernt, das Sie behalten möchten.

In diesem Fall ist eine nette Bash- Option histverify. Wenn Sie shopt -s histverifyund wenn Sie einen Befehl mit dem Knall zurückrufen! , wird er nicht sofort ausgeführt, sondern in die Readline geladen, sodass Sie entscheiden können ihn ausführen möchten oder nicht. Auf diese Weise haben Sie 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 kann erneut gestartet oder bearbeitet werden.

Wenn Ihnen diese Option gefällt, geben Sie sie in Folgendes ein .bashrc:

shopt -s histverify
gniourf_gniourf
quelle