본문 바로가기
리눅스/Part 1 - Learning The Shell

리눅스 기초 | 8. 고급 키보드 트릭

by 객잔주인 2024. 5. 28.
저는 유닉스에 대해 설명할 때 농담삼아 "타이핑을 좋아하는 사람들을 위한 운영체제"라고 하곤 합니다. 커맨드 라인도 있다는 것이 그를 증명합니다. 하지만 커맨드 라인 유저들은 타이핑을 그다지 좋아하지 않습니다. 그렇지 않다면 왜 많은 명령어가 $\texttt{cp}$, $\texttt{ls}$ , $\texttt{mv}$, $\texttt{rm}$ 같이 단어를 축약해서 사용할까요? 사실 커맨드 라인의 가장 소중한 목표 중 하나는 게으름입니다. 자판을 가능한 적게 눌러 원하는 일을 하는 것이죠. 또 다른 목표는 키보드에서 손을 떼어 마우스에 가져갈 필요가 없게 만드는 것입니다. 이번 챕터에서는 키보드 사용을 더 빠르고 효율적이게 만들어주는 $\texttt{bash}$의 기능들을 살펴보겠습니다.

다음과 같은 기능들을 살펴볼 것입니다:

  • $\texttt{clear}$ - 화면을 깨끗하게 정리
  • $\texttt{history}$ - 히스토리 리스트에 있는 과거 내역을 표시

커맨드라인 편집

$\texttt{bash}$는 Readline이라는 라이브러리(다양한 프로그램에서 공통적으로 사용할 수 있는 여러 루틴(코드블록)들)를 이용해서 커맨드라인 편집을 수행합니다. 우리는 이미 이 라이브러리를 사용해보았습니다. 예를 들어, 화살표를 사용해 커서를 움직일 수 있죠. 이외에도 더 많은 기능들이 있습니다. 이 기능들을 우리 작업에 사용할 보조적인 도구로 생각하세요. 전부 다 익힐 필요는 없습니다. 그러나 꽤 유용한 것들이 많이 있습니다. 쓸만하다 싶은 기능들을 골라서 익혀보세요.

몇몇 키 조합(특히 $\texttt{Alt}$ 키를 사용하는 것들)은 GUI에서 다른 기능에 이미 할당돼있을 수 있습니다. 가상 콘솔을 사용하는 경우에는 정상 작동합니다.

커서 움직임

다음 표에서 커서 이동에 사용되는 키를 확인할 수 있습니다:

동작
$\texttt{Ctrl-a}$ 줄의 시작으로 커서를 이둉
$\texttt{Ctrl-e}$ 줄의 끝으로 커서를 이동
$\texttt{Ctrl-f}$ 한 칸 뒤로 커서를 이동; $\rightarrow$와 동일
$\texttt{Ctrl-b}$ 한 칸 앞으로 커서를 이동; $\leftarrow$와 동일
$\texttt{Alt-f}$ 한 단어 뒤로 커서를 이동
$\texttt{Alt-b}$ 한 단어 앞으로 커서를 이동
$\texttt{Ctrl-l}$ 화면을 깨끗이 지우고 커서를 좌상단 코너로 이동. $\texttt{clear}$ 명령어와 동일

텍스트 수정

타이핑하면서 실수가 있을 수 있으니 이를 수정할 수 있는 수단도 필요합니다. 아래 표에서 커맨드라인에서 문자를 편집할 때 사용할 수 있는 키보드 명령어를 확인할 수 있습니다.

동작
$\texttt{Ctrl-d}$ 커서가 위치한 곳에 있는 문자를 삭제
$\texttt{Ctrl-t}$ 커서가 위치한 곳에 있는 문자와 그 앞의 문자의 자리를 교체
$\texttt{Alt-t}$ 커서가 위치한 곳에 있는 단어와 그 앞의 단어의 자리를 교체
$\texttt{Alt-l}$ 단어 내 커서가 위치한 곳부터 마지막 문자까지 소문자로 교체
$\texttt{Alt-u}$ 단어 내 커서가 위치한 곳부터 마지막 문자까지 대문자로 교체

텍스트 잘라내기 및 붙여넣기

Readline 문서에서는 잘라내기(cutting)와 붙여넣기(pasting) 대신 killingyanking이라는 용어를 사용합니다. 잘라내기 된 텍스트는 kill-ring이라고 불리는 버퍼(buffer, 메모리 내의 임시 저장 공간)에 저장됩니다.

동작
$\texttt{Ctrl-k}$ 커서 위치부터 마지막까지 텍스트를 잘라내기
$\texttt{Ctrl-u}$ 커서 위치부터 처음까지 텍스트를 잘라내기
$\texttt{Alt-d}$ 커서 위치부터 현재 단어의 끝까지 잘라내기
$\texttt{Alt-Backspace}$ 커서 위치부터 현재 단어의 처음까지 잘라내기. 만약 커서가 단어의 첫 부분에 위치한다면 그 앞의 단어를 잘라내기
$\texttt{Ctrl-y}$ kill-ring으로부터 텍스트를 현재 커서 위치에 붙여넣기

메타키(Meta Key)

$\texttt{bash}$의 매뉴얼 페이지의 "READLINE" 섹션에 위치한 Redline의 문서를 읽다보면 메타키(meta key) 라는 용어를 만날 수 있습니다. 최근에는 $\texttt{Alt}$키가 그 역할을 하지만 과거에는 항상 그랬던 것은 아니죠.

PC가 보급되기 전에는 모든 사람들이 개인 컴퓨터를 가지고 있던 것은 아닙니다. 대신 터미널이라는 장치를 가지고 있었죠. 터미널은 통신 장치로 디스플레이와 키보드가 있어 텍스트를 화면에 띄우고 커서를 움직일 수 있었습니다. 터미널은 (주로 시리얼 케이블로) 중앙 컴퓨터나 중앙 컴퓨터의 통신 네트워크와 연결되었습니다. 다양한 브랜드의 터미널이 있었고, 각각이 다른 키보드와 디스플레이 기능도 모두 달랐습니다. 모든 터미널은 최소한 ASCII 사용이 가능했기 때문에 소프트웨어 개발자들이 휴대성과 호환성을 보장하기 위해 공통분모인 ASCII를 사용해 프로그램을 작성했습니다. 유닉스 시스템은 다양한 터미널과 서로 다른 디스플레이 기능을 처리하는 복잡한 방법을 가지고 있었습니다. Readline의 개발자들이 추가적인 제어를 위한 키가 존재하지 않을 것을 고려하여, 메타키를 개발했습니다. 현대 키보드에서는$\texttt{Alt}$키가 메타키 역할을 합니다. 터미널상에서는 $\texttt{Esc}$키를 눌렀다 뗌으로써 $\texttt{Alt}$키를 누르고있는 것과 같은 효과를 낼 수 있습니다. 이는 리눅스에서도 여전히 사용할 수 있습니다

자동 완성(Completion)

쉘이 사용자에게 도움을 주는 또 다른 방식으로 자동 완성(completion) 이 있습니다. 자동 완성은 명령을 입력하는 중에 $\texttt{Tab}$키를 눌러 사용할 수 있습니다. 어떻게 작동하는지 보겠습니다. 홈 디렉토리가 다음처럼 생겼다고 가정하겠습니다:

[me@linuxbox ~]$ ls  
Desktop ls-output.txt Pictures Templates Videos Documents Music Public

다음과 같이 작성하고 $\texttt{Enter}$키를 누르지 마세요:

[me@linuxbox ~]$ ls l

이제 $\texttt{Tab}$키를 눌러보세요.

[me@linuxbox ~]$ ls ls-output.txt

쉘이 명령을 자동 완성한 것을 보셨나요? 다른 것도 해보겠습니다. 이번에도 $\texttt{Enter}$를 누르지 마세요.

[me@linuxbox ~]$ ls D

$\texttt{Tab}$을 누르세요.

[me@linuxbox ~]$ ls D

자동 완성이 이루어지지 않았네요. 이는 $\texttt{D}$가 여러 결과와 매칭되기 때문입니다. 자동 완성이 이루어지기 위해서는 더 명확한 "단서"를 주어야 합니다. 다음과 같이 단서를 조금 더 준 다음:

[me@linuxbox ~]$ ls Do

$\texttt{Tab}$키를 누르면:

[me@linuxbox ~]$ ls Documents

자동 완성이 이루어집니다.

이 예시는 자동 완성의 여러 적용 사례 중 가장 자주 사용되는 방법인 경로명의 자동 완성을 보여줍니다. 이 외에도 변수명 자동 완성(단어의 시작이 $\texttt{\$}$인 경우), 사용자명 자동 완성(단어의 시작이 $\texttt{~}$인 경우), 명령어 자동 완성(가장 첫 단어일 경우), 호스트명 자동 완성(단어의 시작이 $\texttt{@}$인 경우)이 가능합니다. 호스트명 자동 완성은 $\texttt{/etc/hosts}$에 등록된 경우에만 작동합니다.

Table 8-4에서는 자동 완성과 관련된 메타키 조합을 볼 수 있습니다.

Table 8-4: Completion Commands

동작
$\texttt{Alt-?}$ 자동 완성이 가능한 모든 경우의 수를 표시합니다. 대부분의 시스템에서 $\texttt{Tab}$키를 두 번 누름으로써 더 쉽게 할 수 있습니다.
$\texttt{Alt-*}$ 자동 완성이 가능한 모든 경우를 모두 삽입합니다. 이는 매칭되는 경우를 여러개 사용하고 싶을 때 유용합니다.

이외에도 잘 알려지지 않은 많은 기능들이 있습니다. $\texttt{bash}$ 매뉴얼 페이지의 "READLINE" 섹션에 그 리스트가 나와있습니다.

프로그래밍 가능한 자동 완성

최신 버전의 $\texttt{bash}$는 프로그래밍 가능한 자동완성(programmable completion) 이라는 기능을 가지고 있습니다. 프로그래밍 가능한 자동완성은 추가적인 자동 완성 규칙을 추가할 수 있도록 해줍니다. 주로 특정한 어플리케이션을 사용하는 데에 편의성을 높이기 위해 사용됩니다. 예를 들어, 애플리케이션이 지원하는 특정 파일 유형이나 명령어의 옵션 목록을 자동 완성할 수 있습니다. 우분투는 꽤 많은 자동 완성 규칙이 기본적으로 탑재되어 있습니다. 이후에 배울 미니 쉘 스크립트와 같은 쉘 함수를 사용해 프로그래밍 가능한 자동 완성을 정의할 수 있습니다. 당장 궁금하다면 이걸 따라해보세요:

$\texttt{set | less}$

배포판에 따라 차이가 있을 수 있습니다.

히스토리 사용하기

챕터1에서 배웠듯이 $\texttt{bash}$는 입력된 명령들을 기억하고 있습니다. 이 명령 리스트는 홈 디렉토리의 $\texttt{.bash_history}$라는 파일에 저장됩니다. 히스토리는 우리가 타이핑해야 할 양을 줄여주기 때문에 꽤 유용합니다. 특히 커맨드 라인 편집과 결합되었을 때 매우 유용합니다.

히스토리 검색하기

프롬프트에 아래와 같이 입력하면 히스토리 리스트를 볼 수 있습니다:

[me@linuxbox ~]$ history | less

기본적으로 $\texttt{bash}$는 500개의 과거 명령을 기억하며 대부분의 최신 배포판에서는 1000개까지 기억하도록 세팅되어있습니다. 이를 조정하는 방법은 챕터 11에서 배워보겠습니다. 예를 들어, $\texttt{/usr/bin}$의 목록을 출력하기 위해 사용했던 명령을 찾고 싶다고 가정해보겠습니다. 다음과 같이 시도해볼 수 있습니다:

[me@linuxbox ~]$ history | grep /usr/bin

여기서 나온 결과들 중 다음과 같은 흥미로운 명령이 있었다고 해보겠습니다:
88 ls -l /usr/bin > ls-output.txt
여기서 $\texttt{88}$은 명령 히스토리에서의 해당 명령의 번호를 나타냅니다. 또 다른 확장인 히스토리 확장(history expansion) 을 이용하면 이 명령을 즉시 사용할 수 있습니다. 위 명령을 사용하기 위해서 다음과 같이 입력하세요:

[me@linuxbox ~]$ !88

$\texttt{bash}$는 $\texttt{!88}$을 확장하여 히스토리 목록의 88번째 줄을 출력할 것입니다. 다음 섹션에서는 히스토리 확장의 다른 형태들을 알아보겠습니다.

또한 $\texttt{bash}$는 히스토리 목록을 점진적으로 검색(사용자가 검색어를 입력할 때마다 즉시 결과가 갱신되는 검색 방식)할 수 있는 기능을 제공합니다. 이는 우리가 검색어를 입력해 나감에 따라 $\texttt{bash}$가 동시에 히스토리 목록에서 검색할 수 있음을 의미합니다. 점진적 검색을 하려면 $\texttt{Ctrl-r}$을 입력한 후 원하는 검색어를 입력합니다. 원하는 명령을 찾았으면 $\texttt{Enter}$를 입력하여 명령을 실행하거나 $\texttt{Crtl-j}$를 입력하여 명령을 복사할 수 있습니다. 다음으로 일치하는 결과(더 과거에 실행한 명령)를 찾고자 하는 경우에는 $\texttt{Ctrl-r}$을 다시 입력합니다. 검색을 종료하려면 $\texttt{Ctrl-g}$나 $\texttt{Ctrl-c}$ 중 하나를 입력합니다. 직접 사용해보겠습니다:

 [me@linuxbox ~]$

먼저 $\texttt{Ctrl-r}$을 입력합니다.

(reverse-i-search)`':

프롬프트가 바뀌면서 역방향 점진적 검색을 실행하고 있음을 확인할 수 있습니다. "역방향"인 이유는 현재와 가까운 시점부터 과거 시점 순으로 검색하기 때문입니다. 다음으로 우리가 검색어를 입력합니다. 이 예시에서는 $\texttt{/usr/bin}$을 입력합니다:

(reverse-i-search)`/usr/bin': ls -l /usr/bin > ls-output.txt

검색어를 입력하는 즉시 검색 결과를 반환합니다. 검색 결과에서 $\texttt{Enter}$를 입력하여 바로 실행하거나 $\texttt{Ctrl-j}$를 눌러 프롬프트에 복사할 수 있습니다. 복사를 해보겠습니다. $\texttt{Ctrl-j}$를 입력해주세요.

[me@linuxbox ~]$ ls -l /usr/bin > ls-output.txt

쉘 프롬프트로 돌아오면서 명령이 복사된 것을 확인할 수 있습니다. Table 8-5에는 히스토리 목록 조작을 위한 단축키 몇 가지를 확인할 수 있습니다.

Table 8-5: History Commands

동작
$\texttt{Ctrl-p}$ 이전 히스토리 항목으로 이동합니다. $\uparrow$와 동일
$\texttt{Ctrl-n}$ 다음 히스토리 항목으로 이동합니다. $\downarrow$와 동일
$\texttt{Alt-<}$ 히스토리 목록의 처음(과거)으로 이동합니다.
$\texttt{Alt->}$ 히스토리 목록의 마지막(현재)으로 이동합니다.
$\texttt{Ctrl-r}$ 역방향 점진적 검색. 현재부터 과거 순으로 히스토리 목록을 검색합니다.
$\texttt{Alt-p}$ 역방향 검색(점진적 X). 검색어를 입력 후 엔터를 입력해 검색
$\texttt{Alt-n}$ 정방향 검색(점진적 X)
$\texttt{Ctrl-o}$ 현재 히스토리 항목을 실행하고 다음 항목으로 넘어감. 과거에 실행했던 연속적인 명령들을 실행하고자 할 때 유용

히스토리 확장

쉘은 $\texttt{!}$를 사용하여 히스토리 목록의 항목들을 위한 특별한 형태의 확장을 지원합니다. 이미 이전에 느낌표과 그 뒤에 숫자를 입력했을 때 히스토리의 항목을 가져올 수 있음을 확인했습니다. 이외에도 Table 8-6의 예시들 처럼 많은 확장 기능들이 있습니다.

Table 8-6: 히스토리 확장 명령들

명령 동작
$\texttt{!!}$ 마지막 명령을 반복합니다. 위쪽 화살표와 엔터를 누르는 것이 더 쉬울 수 있습니다
$\texttt{!number}$ 히스토리 항목 번호와 일치하는 명령을 반복합니다.
$\texttt{!string}$ string으로 시작하는 마지막 명령을 반복합니다.
$\texttt{!?string}$ string이 포함된 마지막 명령을 반복합니다.
$\texttt{!string}$와 $\texttt{!?string}$는 실행될 명령이 100% 확실할 때에만 사용하세요.  

히스토리 확장 메커니즘에는 더 많은 요소들이 있으나 이미 충분히 난해하기 때문에 계속 했다가는 머리가 터져버릴지도 모르겠습니다. $\texttt{bash}$의 매뉴얼 페이지의 HISTORY EXPANSION 섹션에서 굉장히 굉장한 디테일들을 볼 수 있습니다. 자유롭게 살펴보세요!

스크립트(Script)

$\texttt{bash}$의 명령 히스토리 기능에 더해서, 대부분의 리눅스 배포판은 $\texttt{script}$라는 전체 쉘 세션을 기록하여 파일에 저장하는 프로그램이 탑재되어 있습니다. 기본적인 명령 문법은 다음과 같습니다:
script [file]
여기서 file은 기록을 저장하기 위한 파일명입니다. 파일이 명시되어있지 않으면 $\texttt{typescript}$라는 파일이 사용됩니다. $\texttt{script}$의 매뉴얼 페이지를 확인하여 해당 프로그램의 전체 옵션과 기능들을 확인해보세요.

요약

이번 챕터에서는 타이핑을 덜 하도록 도와주는 몇 가지 키보드 트릭을 알아보았습니다. 시간이 지나 커맨드 라인에 더 익숙해지면 이 챕터로 돌아와 이 트릭들을 더 배워보도록 합시다. 지금 당장은 부가적으로 도움이 될만한 요소로만 남겨두자구요

추가 자료