bash_history: komentuj niebezpieczne polecenia: `#`

16

Aby zapobiec rejestrowaniu „niebezpiecznych” poleceń w historii bash, dodałem do mojego .bashrcpliku następujący wiersz :

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

działa to dobrze, ale ma efekt uboczny: nie widzę pełnej historii poleceń wykonywanych na komputerze. Powiedzmy, że mam kilka maszyn do eksperymentów i chcę widzieć wszystkie wykonywane polecenia. Użyłbym bash wewnętrznego historydo wyświetlenia wykonanych poleceń i być może grep dla dzisiejszej daty:

history | grep Sep-28

Chciałbym również rejestrować „niebezpieczne” polecenia, ale umieszczać je #na początku wiersza, aby w razie przypadkowego wykonania polecenia z historii przez pomyłkę nie doszło do żadnych szkód.

Nie mam pojęcia, czy to możliwe.

Aktualizacja i wyjaśnienie:

Głównym powodem, dla którego jest to dla mnie problem, jest to, że zwykle jestem podłączony do mojej maszyny z kilku terminali, a każde polecenie wykonane na jednym terminalu jest natychmiast odczytywane w historii innych terminali. Osiąga się to przez

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

Wyobraźmy sobie, że mam otwarte dwa terminale. W jednym mam jakiś cat /dev/foo > file.outproces. W drugim sprawdzam postęp za pomocą ls -lAhF. Powtarzam ls, naciskając Upi ENTER(czyli ostatnie polecenie z historii). Zaraz po zakończeniu pierwszego polecenia ostatnie polecenie z historii nie jest już ls, ale cat /dev/foo > file.out. Jeśli nie będę ostrożny, uruchomię ponownie kota i zastąpię plik.out.

Chciałbym, aby polecenie kota było poprzedzone znakiem „a” #, aby nie zostało wykonane. Wciąż jednak widziałbym go w historii i mogę go ponownie użyć (jeśli jest to długie polecenie), nie komentując go.

Martin Vegter
źródło
Zamiast ręcznie powtarzać polecenie, możesz użyć watch ls -lAhFlub while sleep 1; do ls -lAhf; done; zamiast oglądać rozmiar pliku, możesz użyć pv /dev/foo > file.out( ivarch.com/programs/pv.shtml ).
deltab

Odpowiedzi:

9

Możesz zrobić coś takiego:

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

Chodzi o to, że przed każdym monitem sprawdzamy ostatni wpis historii ( history 1), a jeśli jest to jeden z niebezpiecznych , usuwamy go ( history -d) i dodajemy z powrotem za #pomocą history -s.

(oczywiście musisz usunąć swoje HISTIGNOREustawienie).

Niepożądany efekt uboczny, że choć jest to, że zmienia się czas historię tych rm, mv... polecenia.

Aby to naprawić, alternatywą może być:

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

Tym razem rejestrujemy czas ostatniej historii, a aby dodać linię historii, używamy history -rz pliku tymczasowego (dokument tutaj), który zawiera znacznik czasu.

Chciałbyś, żebyś fixhistto zrobił przed swoim history -a; history -c; history -r. Niestety obecna wersja bashma błąd, history -aktóry nie zapisuje tej dodatkowej linii, którą dodaliśmy. Rozwiązaniem jest napisanie go zamiast tego:

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

Oznacza to, że sami dodamy komentowane polecenie do HISTFILE, zamiast pozwolić history -ana to.

Stéphane Chazelas
źródło
czy możesz wyjaśnić, co cmd=${cmd#*[0-9] }robi? I gdzie dokładnie należy umieścić fixhistw moim PROMPT_COMMAND? W tej chwili zawiera jużPROMPT_COMMAND="history -a; history -c; history -r"
Martin Vegter
Jaka jest rola HISTTIMEFORMAT=/? Już ustawiłem export HISTTIMEFORMAT="%b-%d %H:%M ". Nawiasem mówiąc, kiedy dodam twój kod do mojego $HOME/.bashrc, nic się nie dzieje.
Martin Vegter,
HISTTIMEFORMAT=/jest tylko dla tego jednego wywołania, historyaby uzyskać wynik, np. 123 /rm xtam, gdzie łatwiej jest wyodrębnić cmd. Wystąpił problem z niektórymi wersjami bashgdzie $HISTCMDbył podrobiony w tym kontekście wypróbować nową wersję.
Stéphane Chazelas,
nadal nie działa. Zastanawiam się, czy #kod nie działa jako komentarz .bashrc?
Martin Vegter,
1
@MartinVegter, tak, w przypadku zmodyfikowanych wpisów historii bashwstawia *po numerze numer, którego nie oczekiwano. Powinien zostać teraz naprawiony.
Stéphane Chazelas
11

Nie zamierzam dokładnie odpowiedzieć na twoje pytanie, ale może dam alternatywne rozwiązanie twojego problemu.

Jeśli dobrze rozumiem, martwisz się błędami, które możesz popełnić, pisząc np. !rmJeśli zdarzyło się, że poprzednie rmpolecenie w historii usuwa coś, co chciałbyś zachować.

W tym przypadku fajną opcją jest bashhistverify . Jeśli ty shopt -s histverifyi jeśli przywołasz polecenie z hukiem !, nie zostanie ono wykonane natychmiast, ale zostanie załadowane do linii odczytu, abyś mógł albo zdecydować się je wykonać, albo nie, a to również daje możliwość edycji.

Spróbuj:

  • Bez 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
  • Z histverify:

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

    W takim przypadku kursor będzie znajdować się na końcu wiersza, gotowy do ponownego uruchomienia - lub nie - lub do edycji.

Jeśli podoba ci się ta opcja, umieść ją w .bashrc:

shopt -s histverify
gniourf_gniourf
źródło