Command History
터미널을 열었을때 실행되는 interactive shell 에서는 명령 history 기능을 사용할 수 있습니다. 명령 history 는 이전에 한번 사용했던 명령을 다시 타입 할 필요 없이 재사용할 수 있게 해줍니다. 터미널 별로 history list 가 생성되며 사용한 명령들이 목록에 추가됩니다. 터미널 종료 시에는 shopt -s histappend
옵션 설정에 따라 $HISTFILE 에 현재 history list 가 저장됩니다.
history 확장에 사용되는 문자는 !
인데
명령 행상 어느 위치에서든지 !
문자에 이어 공백 없이 다른 문자나 숫자가 오면
history 확장이 됩니다.
심지어 double quotes 안에서도 확장이 일어나므로 주의해야 합니다.
한가지 예외적인 경우는 !=
인데 [
명령에서 연산자로 사용되므로 history 확장에서 제외됩니다.
!
문자가 명령이름의 위치에 오고 뒤이어 공백이 올경우는 shell keyword 로 인식되어 logical NOT 의 기능을 합니다.
$ find ! -size 0 # ! 문자 뒤에 공백이 오므로 OK
$ find !-size 0 # 이것은 history 확장 대상으로 명령이 정상적으로 실행되지 않는다.
bash: !-size: event not found
$ ! test -s emptyfile # logical NOT 쉘 키워드 (! 문자가 명령 위치에 오고 이어 공백이 오므로)
Command history 확장은 shell prompt 상에서만 작동하는 기능입니다.
그러므로 Non-interactive shell 인 스크립트 실행시에는 기본적으로 disable 됩니다.
명령 라인을 찾는 방법
프롬프트에서 history
명령을 실행하면 현재 history list 에 있는 목록들이 번호와 함께 표시됩니다. 이 번호는 해당 명령 라인을 지정할때 사용됩니다.
!n
n 번 명령을 리턴합니다.
$ !2145
lsb_release -d
Description: Ubuntu 15.04
$ echo command history : !2145
command history : lsb_release -d
!!
바로 이전 명령을 나타냅니다.
$ history
...
3 ps f
4 cat README.md
5 find * -name '*.log' -size +10M -exec rm -f {} \;
$ !!
find * -name '*.log' -size +10M -exec rm -f {} \;
!-n
이전 n 번째 명령을 나타냅니다.
$ history
...
3 date
4 ps f
5 find * -name '*.log'
$ !-1
find * -name '*.log'
$ !-2
ps f
$ !-3
date
!string
명령 이름이 string 으로 시작하는 가장 최근 명령을 찾습니다.
$ history
...
3 find * -name '*.log' -size +10M -exec rm -f {} \;
4 find * -name '*.log'
5 ps f
$ !fi
find * -name '*.log'
!?string[?]
이건 명령이름을 검색하는게 아니고 전체 명령라인 중에 string 이 포함돼 있는지를 찾습니다. 가장 최근에 매칭이 되는 라인을 리턴합니다.
$ history
...
3 find * -name "*.tmp" -o -name "*.old"
4 find * -name "*.tmp"
5 find * -name "*.log"
$ !?tmp
find * -name "*.tmp"
# 뒤에 '?' 를 붙이면 연이어 명령을 작성할 수 있다.
$ !?tmp? -exec rm -f {} \;
find * -name "*.tmp" -exec rm -f {} \;
# 뒤에 '?' 를 안붙일 경우 오류
$ !?tmp -exec rm -f {} \;
bash: !?tmp -exec rm -f {} \;: event not found
^old^new
이전 명령에서 처음에 매칭되는 하나만 변경됩니다. !!:s/string1/string2/
와 같습니다.
$ mkdir -p test/exp/scenario/
$ ^exp^lab
$ mkdir -p test/lab/scenario/
!#
이것은 이전 명령이 아니라 현재 프롬프트 상에서 작성 중인 명령을 나타냅니다.
$ echo 111 222 333 !#:1 # 첫번째 인수
$ echo 111 222 333 111
$ echo 111 222 333 !#:2 # 두번째 인수
$ echo 111 222 333 222
$ mv long/path/name/oldname !#$ # 마지막 인수
$ mv long/path/name/oldname long/path/name/oldname
찾은 명령 라인에서 원하는 인수를 지정하는 방법
명령 라인을 지정한 후에 :
문자를 붙인 후 원하는 인수들을 지정할 수 있습니다.
0 번은 명령을 나타내고 이후 인수들은 1, 2, 3 ... 번으로 지정할 수 있습니다.
$ touch home foo bar tmp log
$ !tou:0 # 0 번은 명령
touch
$ !tou:1 # 1 번은 첫번째 인수
home
$ !tou:2 # 2 번은 두번째 인수
foo
$ !tou:2-4 # '-' 를 이용해 범위를 지정
foo bar tmp
$ !tou:* # '*' 는 모든 인수를 나타냅니다.
home foo bar tmp log
$ !tou:3* # '3*' 은 3 번째 인수부터 끝까지
bar tmp log
$ !tou:^ # '^' 는 첫번째 인수를 나타내며 !tou:1 와 같습니다.
home
$ !tou:$ # '$' 는 마지막 인수를 나타냅니다.
log
명령라인 지정을 생략하면 바로 이전 명령이 사용됩니다.
$ history
...
3 date
4 ps f
5 echo 11 22 33 44
$ !:0
echo
$ !:1
11
$ !:2-4
22 33 44
$ !*
11 22 33 44
$ !^
11
$ !$
44
지정한 라인, 인수에 modifiers 적용하기
라인을 지정한 뒤, 또는 인수들을 지정한 뒤에 :
문자를 붙인 후 modifiers 를 적용시킬 수 있습니다.
s/old/new/
지정한 명령라인에서 old 에 해당하는 스트링을 new 로 변경합니다. new 부분에 &
문자가 오면 old 로 대체됩니다. 기본적으로 처음에 매칭되는 하나만 적용되며 모두에 적용하려면 g
옵션을 추가합니다.
$ touch foo1 bar1 tmp1
# 처음 하나만 변경된다
$ !:s/1/2
touch foo2 bar1 tmp1
# 'g' 옵션을 추가하면 모두 변경된다.
$ !:gs/1/2
touch foo2 bar2 tmp2
파일 경로명에서 파일명 분류하기
$ cat /home/foo/readme.txt
$ !:h # 'h' 는 head 를 의미
cat /home/foo # cat 명령이 함께 포함됨
$ !:t # 't' 는 tail 을 의미
readme.txt
$ !:h/AA # 연이어 명령을 작성할 수 있다.
cat /home/foo/AA
$ !:1:h # ':1' 인수에서 head 를 구함
/home/foo
파일 경로명에서 확장자 분류하기
$ cat /home/foo/readme.txt
$ !:r # 확장자를 remove
cat /home/foo/readme
$ !:r.old
cat /home/foo/readme.old
$ !:e # 확장자 (extension) 만 구함.
.txt
결과물 quoting 하기
$ echo foo bar tmp
$ !:2-3:q
'bar tmp'
$ !:q # 전체 라인이 quote 된다.
'echo foo bar tmp'
$ !:*:x # space 로 분리되어 각각 quote 된다.
'foo' 'bar' 'tmp'
실행 금지하기
history 확장이 되면 바로 결과물이 실행되는데 p
옵션을 붙이면 결과만 표시하고 실행을 금지할 수 있습니다.
$ mv foo bar
$ !mv:s/foo/boo/:p # 결과만 프린트되고 실행은 되지 않는다
mv boo bar
Double quotes 과 history 확장
History 확장은 double quotes 내에서도 일어나므로 주의해야 합니다.
$ lsb_relase -d
Description: Ubuntu 15.04
$ echo command history : !lsb
command history : lsb_release -d
$ echo "hello!lsb world" # double quotes 에서도 history 확장이 된다.
hellolsb_relase -d world"
$ echo "hello!516world"
hellolsb_release -dworld
$ echo 'hello!516world' # single quotes 에서는 확장이 안된다.
hello!516world
Double quotes 사용시 다음과 같이 history 확장을 회피할 수 있습니다.
$ echo "hello"\!"lsb world"
hello!lsb world
$ edho "hello"'!'"516world"
hello!516world
# sed 명령으로 "2016 12-10" 와 매칭되지 않는 라인을 삭제하려고 하지만 !d 에서
# history 확장이 되어 정상적으로 실행되지 않는다.
$ search="2016 12-10"
$ sed "/$search/!d" file.txt
ERR
$ sed "/$search/"'!d' file.txt
# 다음과 같은 경우도 double quotes 내에 있으므로 history 확장 대상입니다.
$ AA="$( echo 111 | sed -r '/222/!{ s/.*/xxx/ }' )"
bash: !{s/.*/xxx/}': event not found
$ AA="$( echo 111 | sed -r '/222/'\!'{ s/.*/xxx/ }' )"
$ echo $AA
xxx
History 관련 환경 변수
HISTIGNORE
history 리스트에 저장할때 제외시킬 명령패턴을
:
로 분리하여 등록합니다.HISTIGNORE='ls:ls -al:cd:bg:fg:history'
HISTFILESIZE
history 파일에 저장될 최대 라인수를 나타냅니다.
HISTSIZE
history 리스트에 기억될 최대 명령수를 나타냅니다. 디폴트 값은 500 입니다.
HISTFILE
history 를 저장할 파일을 지정합니다.
HISTCONTROL
명령 history 의 작동방식을
:
로 분리하여 설정할수 있습니다.- ignorespace : space 로 시작하는 명령라인을 history 에 저장하지 않습니다.
- ignoredups : 이전 history 명령과 중복될경우 저장하지 않습니다.
- ignoreboth : ignorespace:ignoredups 와 같습니다.
- erasedups : 이전 모든 history 라인을 비교하여 중복된 history 를 제거한후 저장합니다.
HISTTIMEFORMAT
history 번호에 이어 timestamp 를 붙일 수 있습니다. history file 에도 저장됩니다.
예) export HISTTIMEFORMAT="%F %T "
History 관련 옵션
Set
history
명령 history 기능을 enable, disable 할 수 있습니다.
-H | histexpand
!
문자를 이용한 history 확장 기능을 제공합니다.set -o history
이 설정돼있어야 사용할 수 있습니다.
Shopt
cmdhist
multiple-line 명령을 작성할 경우 명령 줄들이 각각 다른 history 번호로 할당돼서 다음에 재사용하기가 어려운데 이 옵션을 사용하면 newline 을
;
로 치환해서 저장해 줍니다.lithist
옵션과 같이 사용하면 newline 도 그대로 유지됩니다.
lithist
multiple-line 명령을 작성할 경우 newline 을 유지해 줍니다.
histreedit
history 확장이 실패할 경우 입력했던 내용이 없어지지 않고 다시 수정할수 있는 기회를 줍니다.
histverify
history 확장된 명령을 바로 실행하지 않고 필요시 수정할 수 있게 enter 를 칠 기회를 줍니다.
histappend
shell 을 exit 할때
HISTFILE
변수에 설정돼 있는 파일에 현재 history list 를 append 합니다. off 이면 overwrite 합니다.터미널이 비정상적으로 종료할 경우 history list 가 저장되지 않습니다. 그럴 경우를 위해
PROMPT_COMMAND='history -a'
를 설정해 사용할 수 있습니다.
History builtin 명령
history [-c] [-d offset] [n] or history -anrw [filename] or history -ps arg [arg...]
현재 세션의 history list 를 관리하며 history file 을 read 하거나 write 해서 여러 터미널 세션 간에 history 를 동기화할 수 있습니다.
옵션 | 설명 |
---|---|
-c | 현재 세션의 history list 를 모두 삭제합니다. |
-d offset | offset 위치의 항목을 삭제합니다. |
-r | history file 을 읽어들이고 내용을 현재 세션의 history list 에 append 합니다 |
-n | history file 에서 아직 읽어 들이지 않은 항목이 있으면 모두 읽어 들입니다. |
-a | 현재 세션의 history list 를 history file 에 append 합니다. |
-w | 현재 세션의 history list 를 history file 에 write 합니다. |