bash_history: коментуйте небезпечні команди: `#`


16

Щоб запобігти входженню "небезпечних" команд в історію bash, я додав у свій .bashrcфайл наступний рядок :

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

це працює добре, але це має побічний ефект: я не бачу повної історії команд, виконаних на машині. Скажімо, у мене є кілька машин для експериментів, і я хочу мати можливість бачити всі команди виконані. Я використовував би bash Internal historyдля відображення виконаних команд, а можливо, і grep для сьогоднішньої дати:

history | grep Sep-28

Що я хотів би зробити - це також записувати "небезпечні" команди, але ставити a #на початку рядка, так що якщо я випадково виконаю команду з історії помилково, ніякої шкоди не буде зроблено.

Я поняття не маю, чи це можливо.

Оновлення та уточнення:

Основна причина, чому це проблема для мене, полягає в тому, що я зазвичай підключений до своєї машини з декількох терміналів, і будь-яка команда, що виконується на одному терміналі, негайно читається в історію інших терміналів. Це досягається шляхом

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

Уявімо, що у мене відкриті два термінали. В одному у мене cat /dev/foo > file.outпрацює якийсь процес. У другій я перевіряю прогрес ls -lAhF. Я продовжую повторюватися lsнатисканням Upі ENTER(тобто останньою командою з історії). Як тільки перша команда закінчується, остання команда з історії вже не є ls, але cat /dev/foo > file.out. Якщо я не обережний, я знову запускаю кота і перепишу файл.out.

Я хотів би досягти того, щоб команда cat передувала знаку a #, щоб вона не виконувалася. Однак я все-таки бачу це в історії і можу повторно використовувати його (якщо це довга команда), не коментуючи його.


Замість повторення команди вручну, ви можете використовувати watch ls -lAhFабо while sleep 1; do ls -lAhf; done; замість перегляду розміру файлу ви можете використовувати pv /dev/foo > file.out( ivarch.com/programs/pv.shtml ).
дельтаб

Відповіді:


9

Ви можете зробити щось на кшталт:

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

Ідея полягає в тому, що перед кожним підказкою ми перевіряємо останню запис історії ( history 1), і якщо вона є однією з небезпечних , ми видаляємо її ( history -d) і додаємо її назад з #допомогою history -s.

(очевидно, вам потрібно видалити HISTIGNOREналаштування).

Хоча небажаний побічний ефект цього полягає в тому, що він змінює час історії тих rm, mv... команд.

Щоб виправити це, альтернативою може бути:

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

Цього разу ми записуємо час останньої історії, і щоб додати назад рядок історії, ми використовуємо history -rз тимчасового файлу (тут документ), який включає часову позначку.

Ви хочете, щоб це fixhistбуло виконано перед вашим history -a; history -c; history -r. На жаль, у поточній версії bashпомилка, history -aяка не врятує додаткову рядок, який ми додали. Робота навколо полягає в тому, щоб написати це замість:

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

Тобто, щоб самі додати коментовану команду до ІСТИФІЛЮ, а не дозволяти history -aїй це робити.


чи можете ви поясніть, що cmd=${cmd#*[0-9] }робить? І де саме я повинен поставити fixhistна мою PROMPT_COMMAND? Наразі він уже міститьPROMPT_COMMAND="history -a; history -c; history -r"
Мартіна Вегтера

яка роль HISTTIMEFORMAT=/? Я вже встановив export HISTTIMEFORMAT="%b-%d %H:%M ". До речі, коли я додаю ваш код у свій $HOME/.bashrc, нічого не відбувається.
Мартін Вегтер

HISTTIMEFORMAT=/є лише для одного виклику, historyщоб отримати вихід, як-от 123 /rm xтам, де легше витягнути cmd. Виникла проблема з деякими версіями про те, bashде $HISTCMDбуло помилково в цьому контексті, спробуйте нову версію.
Стефан Шазелас

досі не працює. Мені цікаво, чи не #в коді не діють як коментарі .bashrc?
Мартін Вегтер

1
@MartinVegter, так, для змінених записів історії bashвставляє *після номера історії, якого не очікували. Слід виправити зараз.
Стефан Шазелас

11

Я не збираюся точно відповідати на ваше запитання, але, можливо, дам вам альтернативне рішення вашої проблеми.

Якщо я правильно розумію, ви стурбовані помилками, які ви могли зробити, ввівши, наприклад, !rmякщо трапилося, що попередня rmкоманда в історії видаляє щось, що ви хотіли б зберегти.

У цьому випадку хороший варіант баштиhistverify . Якщо ви shopt -s histverify, і якщо ви згадуєте команду з вибухом !, вона не буде виконана негайно, а буде завантажена у лінію читання, щоб ви могли вирішити її виконати чи ні, і це також дає можливість редагувати її.

Спробуй це:

  • Без 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
  • З histverify:

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

    У цьому випадку курсор у кінці рядка буде готовий запустити його знову - чи ні - або відредагувати.

Якщо вам сподобався цей варіант, поставте його у свій .bashrc:

shopt -s histverify
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.