Bash 기록에 "위험한" 명령이 기록되는 것을 방지하기 위해 파일에 다음 줄을 추가했습니다 .bashrc
.
HISTIGNORE='rm *:mv *:cp *:cat*>*:pv*>*'
이것은 잘 작동하지만 부작용이 있습니다. 컴퓨터에서 실행된 명령의 전체 기록을 볼 수 없습니다. 실험용 컴퓨터가 여러 대 있고 실행되는 모든 명령을 볼 수 있기를 원한다고 가정해 보겠습니다. bash 내부를 사용 history
하여 실행된 명령을 표시하고 아마도 오늘 날짜에 대한 grep을 사용할 것입니다.
history | grep Sep-28
내가 원하는 것은 "위험한" 명령도 기록하되 #
행의 시작 부분에 a를 넣어 실수로 기록에서 명령을 실행하더라도 손상이 발생하지 않도록 하는 것입니다.
이것이 가능한지 모르겠습니다.
업데이트 및 설명:
이것이 나에게 문제가 되는 주된 이유는 일반적으로 여러 터미널에서 내 컴퓨터에 연결되어 있고 한 터미널에서 실행된 모든 명령이 즉시 다른 터미널의 기록으로 읽혀지기 때문입니다. 이는 다음에 의해 달성됩니다.
PROMPT_COMMAND="history -a; history -c; history -r"
두 개의 터미널이 열려 있다고 상상해 봅시다. 하나에는 일부 cat /dev/foo > file.out
프로세스가 실행 중입니다. 두 번째에서는 ls -lAhF
. and (즉, 기록의 마지막 명령)를 ls
눌러 계속 반복합니다 . 첫 번째 명령이 완료되자마자 기록의 마지막 명령은 더 이상 가 아니라 입니다 . 조심하지 않으면 cat을 다시 시작하고 file.out을 덮어쓰게 됩니다.UpENTERls
cat /dev/foo > file.out
내가 달성하고 싶은 것은 cat 명령 앞에 a가 붙어서 #
실행되지 않도록 하는 것입니다. 그러나 기록에서 여전히 볼 수 있으며 주석을 제거하여 (긴 명령인 경우) 재사용할 수 있습니다.
답변1
나는 귀하의 질문에 정확하게 대답하지는 않지만 귀하의 문제에 대한 대체 솔루션을 제공할 수도 있습니다.
제가 올바르게 이해했다면, 입력으로 인해 발생할 수 있는 실수에 대해 우려하시는 것입니다. 예를 들어 기록의 이전 명령이 유지하고 싶은 항목을 제거한 !rm
경우가 있습니다 .rm
이 경우에는 좋은세게 때리다옵션은 입니다 histverify
. 을 shopt -s histverify
실행하고 bang 을 사용하여 명령을 호출하면 !
즉시 실행되지는 않지만 readline에 로드되어 실행 여부를 결정할 수 있으며 이를 편집할 수도 있습니다.
시도 해봐:
없이
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
답변2
다음과 같이 할 수 있습니다:
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
하고 해당 항목이위험한) 을 삭제 하고 with 를 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
타임스탬프가 포함된 임시 파일(here 문서)을 사용합니다.
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
이는 주석 처리된 명령을 그대로 두는 대신 HISTFILE에 직접 추가하는 것입니다 history -a
.
답변3
그만큼Stéphane의 답변이 수락되었습니다.정말 놀랍습니다. 아래는 패턴 일치가 약간 다른 것을 채택한 것입니다.
정규식 일치( )를 사용하는 if
대신 문을 사용하면 아래 코드는 다음 논리에 따라 작동합니다.case
[[ "$cmd" =~ <REGEX> ]]
- 다음과 같은 경우에는 기록에 추가하지 마세요.
- 명령줄은 2개 이상의 연속 공백으로 시작하거나 끝납니다.
예:<spc><spc>history
- 명령줄은 2개 이상의 연속 공백으로 시작하거나 끝납니다.
- 다음과 같은 경우에 추가 명령이 주석 처리되었습니다.
rm
또는mv
cli 스위치를 사용한 명령- 명령줄은
:;
단일 공백으로 시작하거나 다음으로 끝납니다.;:
예:
rm -R /tmp/somedir
echo "doing something dangerous I don't want to trip by accident";:
여기에는 캐비에트도 있는데, 예를 들면 다음과 같습니다.
echo ";rm -something"
- 그렇지 않으면 기록에 추가하세요
.rm somefile
echo ";rm -something"
코드
_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"