bash_history: 위험한 명령 주석 처리: `#`

bash_history: 위험한 명령 주석 처리: `#`

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을 덮어쓰게 됩니다.UpENTERlscat /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
  • 다음과 같은 경우에 추가 명령이 주석 처리되었습니다.
    • rm또는 mvcli 스위치를 사용한 명령
    • 명령줄은 :;단일 공백으로 시작하거나 다음으로 끝납니다. ;:
      예:
    • 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"

관련 정보