Bash 히스토리는 매우 강력한 기능입니다. Bash 히스토리 확장을 효과적으로 활용하는 방법을 익히면 Linux 명령줄에서 작업 생산성이 크게 향상됩니다.
Bash 히스토리 확장의 3가지 핵심 요소
이 글에서는 다음 세 가지 Bash 히스토리 확장 기능을 활용한 15가지 실전 예제를 소개합니다.
- 이벤트 지정자(Event Designators) – 히스토리에서 특정 명령을 참조합니다. 느낌표(!)로 시작합니다.
- 단어 지정자(Word Designators) – 히스토리 항목의 특정 단어를 참조합니다. 보통 이벤트 지정자와 조합하여 사용하며, 두 지정자는 콜론(:)으로 구분합니다.
- 수정자(Modifiers) – 이벤트 또는 단어 지정자가 수행한 치환 결과를 가공합니다.
이 글은 연재 중인 Bash 튜토리얼 시리즈의 일부입니다.
모든 히스토리 항목을 확인하려면 history 명령어를 사용하면 됩니다. 이 명령은 이전에 실행한 모든 명령과 함께 히스토리 목록에서의 번호를 함께 표시해 줍니다.
$ history 1 tar cvf etc.tar /etc/ 2 cp /etc/passwd /backup 3 ps -ef | grep http 4 service sshd restart 5 /usr/local/apache2/bin/apachectl restart
Bash 히스토리 이벤트 지정자
1. !n으로 히스토리의 특정 명령 실행하기
이전에 실행했던 명령을 다시 입력할 필요 없이, 히스토리 번호만으로 빠르게 재실행할 수 있습니다.
예를 들어 4번 명령을 실행하려면 다음과 같이 입력합니다. 해당 명령이 화면에 표시된 후 즉시 실행됩니다.
$ !4 service sshd restart
두 번 전에 입력한 명령을 실행하려면 다음과 같이 합니다.
$ !-2
바로 직전 명령을 실행하려면 아래 방법 중 하나를 사용하세요.
$ !! $ !-1
또한 <Ctrl>-P 키(기본 emacs 모드인 경우)를 눌러도 이전 명령으로 이동할 수 있습니다.
'set -o vi'로 명령줄 편집을 vi 스타일로 설정했다면 <Esc>-k 키로 이전 명령을 불러올 수 있습니다.
2. !string과 !?string으로 키워드 기반 명령 실행하기
키워드를 사용해서 히스토리 속 명령을 실행할 수도 있습니다.
다음 예제는 키워드 "ps"로 시작하는 이전 명령을 검색하여 실행합니다. 여기서는 "ps -ef | grep http" 명령을 찾아 실행합니다.
$ !ps ps -ef | grep http
다음 예제는 키워드 "apache"를 포함하는 이전 명령을 검색하여 실행합니다. 여기서는 "/usr/local/apache2/bin/apachectl restart" 명령을 찾아 실행합니다.
$ !?apache /usr/local/apache2/bin/apachectl restart
3. ^str1^str2^로 이전 명령의 문자열 교체하기
다음 예제에서는 먼저 ls 명령으로 파일을 확인했습니다. 이후 파일 내용을 보고 싶어졌을 때, 긴 파일명을 다시 입력하는 대신 이전 명령의 "ls"를 "cat"으로 교체하면 됩니다.
$ ls /etc/cron.daily/logrotate $ ^ls^cat^ cat /etc/cron.daily/logrotate
참고: 더 많은 Bash 히스토리 활용 팁이 필요하다면 관련 튜토리얼을 참고하세요. 히스토리에 타임스탬프를 표시하는 방법과 HISTTIMEFORMAT, HISTSIZE, HISTFILE, HISTCONTROL, HISTIGNORE 등 다양한 히스토리 관련 환경 변수 사용법을 설명합니다.
Bash 히스토리 단어 지정자
단어 지정자는 새로운 명령을 입력하면서 이전에 실행한 명령의 인수를 그대로 가져와 사용하고 싶을 때 매우 유용합니다. 몇 가지 예제를 살펴보겠습니다.
4. :^로 명령의 첫 번째 인수 가져오기
다음 예제에서는 "ls -l" 명령의 인수로 "!cp:^"를 사용했습니다. "!cp:^"는 "cp"로 시작하는 이전 명령을 찾아 그 첫 번째 인수를 반환합니다.
$ cp /etc/passwd /backup $ ls -l !cp:^ ls -l /etc/passwd
바로 직전 명령의 첫 번째 인수를 가져오려면 다음과 같이 합니다.
$ ls -l !!:^
5. :$로 명령의 마지막 인수 가져오기
다음 예제에서는 "ls -l" 명령의 인수로 "!cp:$"를 사용했습니다. "!cp:$"는 "cp"로 시작하는 이전 명령을 찾아 그 마지막 인수를 반환합니다.
$ cp /etc/passwd /backup $ ls -l !cp:$ ls -l /backup
바로 직전 명령의 마지막 인수를 가져오려면 다음과 같이 합니다.
$ ls -l !!:$
6. :n으로 명령의 n번째 인수 가져오기
다음 예제에서는 "ls -l" 명령의 인수로 "!tar:2"를 사용했습니다. "!tar:2"는 "tar"로 시작하는 이전 명령을 찾아 그 두 번째 인수를 반환합니다.
$ tar cvfz /backup/home-dir-backup.tar.gz /home $ ls -l !tar:2 ls -l /backup/home-dir-backup.tar.gz
7. :*로 명령의 모든 인수 가져오기
다음 예제에서는 "ls -l" 명령의 인수로 "!cp:*"를 사용했습니다. "!cp:*"는 "cp"로 시작하는 이전 명령을 찾아 모든 인수를 반환합니다.
$ cp /etc/passwd /backup $ ls -l !cp:* ls -l /etc/passwd /backup
8. !%로 최근 검색한 단어 참조하기
앞서 설명했듯이 "!?apache"는 "apache"라는 키워드를 포함하는 이전 명령을 찾아 실행합니다.
$ /usr/local/apache2/bin/apachectl restart $ !?apache /usr/local/apache2/bin/apachectl restart
!%는 바로 앞의 "?" 검색에서 일치한 단어 전체를 참조합니다.
예를 들어 이전에 "?apache"로 검색했다면, "!%"는 "/usr/local/apache2/bin/apachectl"이라는 단어 전체와 일치합니다. 이 문맥에서는 "/"도 한 단어의 일부로 취급된다는 점에 유의하세요.
따라서 다음과 같이 실행하면 apache를 중지시킬 수 있습니다.
$ !% stop /usr/local/apache2/bin/apachectl stop
9. x-y 형식으로 인수 범위 가져오기
다음 예제에서는 "ls -l" 명령의 인수로 "!tar:3-5"를 사용했습니다. "!tar:3-5"는 "tar"로 시작하는 이전 명령을 찾아 3번째부터 5번째까지의 인수를 반환합니다.
$ tar cvf home-dir.tar john jason ramesh rita $ ls -l !tar:3-5 ls -l john jason ramesh
2번째부터 끝까지 모든 인수를 가져오려면 다음과 같이 합니다.
$ ls -l !tar:2-$
참고로 아래 내용도 알아두면 유용합니다.
- !!:* – 직전 명령의 모든 인수를 가져옵니다.
- !!:2* – 2번째 인수부터 끝까지 모든 인수를 가져옵니다.
- !!:2-$ – 위와 동일하게 2번째 인수부터 끝까지 모든 인수를 가져옵니다.
- !!:2- – 2번째 인수부터 마지막 인수를 제외한 나머지 인수를 가져옵니다.
Bash 히스토리 수정자
수정자는 단어 지정자 뒤에 붙여서 사용합니다. 아래 예제들을 통해 확인해 보세요.
10. :h로 경로에서 파일명 부분 제거하기
다음 예제에서 "!!:$:h"는 직전 명령의 마지막 인수를 가져온 뒤 경로의 마지막 부분(파일명)을 제거하고 전체 디렉터리 경로만 남깁니다.
$ ls -l /very/long/path/name/file-name.txt $ ls -l !!:$:h ls -l /very/long/path/name
11. :t로 경로에서 선행 경로 부분 제거하기
이것은 바로 앞 예제와 정반대 동작입니다.
다음 예제에서 "!!:$:t"는 직전 명령의 마지막 인수를 가져온 뒤 선행 경로를 모두 제거하고 파일명만 남깁니다.
$ ls -l /very/long/path/name/file-name.txt $ ls -l !!:$:t ls -l file-name.txt
12. :r로 파일 확장자 제거하기
다음 예제에서 "!!:$:r"은 직전 명령의 마지막 인수를 가져온 뒤 ".확장자" 부분만 제거합니다. 여기서는 .txt가 제거됩니다.
$ ls -l /very/long/path/name/file-name.txt $ ls -l !!:$:r ls -l /very/long/path/name/file-name
13. :s/str1/str2/로 sed 방식 문자열 치환하기
앞서 살펴본 "^원본^교체^" 방식 대신, 아래 예제처럼 sed와 유사한 치환 문법을 Bash 히스토리에서 사용할 수 있습니다. 이 방법이 더 기억하기 쉬울 수 있습니다. !!는 직전 명령을 호출하고, ":s/원본문자열/교체문자열/"이 sed 스타일의 문자열 치환 문법입니다.
$ !!:s/ls -l/cat/
아래와 같이 g 플래그(s 플래그와 함께)를 사용하면 전역 치환도 가능합니다. 여러 단어를 잘못 입력했을 때 한 번에 모두 수정하고 명령을 다시 실행할 때 유용합니다.
다음 예제에서는 실수로 passwd 대신 password를 두 번 입력했습니다.
$ cp /etc/password /backup/password.bak
이를 수정하려면 다음과 같이 전역 히스토리 치환을 수행하면 됩니다.
$ !!:gs/password/passwd/ cp /etc/passwd /backup/passwd.bak
14. :&로 이전 치환 빠르게 반복하기
위와 같이 Bash 히스토리 치환을 성공적으로 실행한 적이 있다면, :&를 사용해 동일한 치환을 빠르게 반복할 수 있습니다.
다른 명령에서 또 실수로 passwd 대신 password를 입력했다고 가정해 보겠습니다.
$ tar cvf password.tar /etc/password
이제 명령을 다시 입력하거나 "gs/password/passwd"를 반복할 필요 없이, ":&"만 사용하면 마지막 치환을 재사용합니다. 전역으로 재사용하려면 ":g&"를 사용하세요.
$ !!:g& tar cvf passwd.tar /etc/passwd
15. :p로 명령 실행 없이 출력만 하기
복잡한 히스토리 치환을 수행할 때 실행 전에 최종 명령을 미리 확인하고 싶다면 이 기능이 매우 유용합니다.
다음 예제의 "!tar:3-:p"는 실제로 명령을 실행하지 않습니다.
":p"가 지정되었기 때문에 치환만 수행하고 새 명령을 화면에 표시합니다. Bash 히스토리 확장 결과를 확인한 후 의도한 명령이 맞다면 ":p"를 제거하고 다시 실행하면 됩니다.
$ tar cvf home-dir.tar john jason ramesh rita $ tar cvfz new-file.tar !tar:3-:p tar cvfz new-file.tar john jason ramesh