터미널 명령어들을 짧게 만들어보자 - alias와 shell scripting

터미널 명령어들을 짧게 만들어보자 - alias와 shell scripting

자주쓰는 명령어들이 있는가? 그런데, 그게 너무 길진 않은가?

리눅스를 장기간 사용하다보면 자주 사용하는 명령어들이 생기기 마련이다. 나는 다음의 명령어들을 자주 사용한다.

  1. 현재 경로에서 git commit 및 push 작업 수행:
    git add . && git commit -m "today's work" && git push
  2. 새로운 blog글 작성을 위한 tmux 환경 세팅 후 해당 세션 열기:
    tmux new-session -d -s "write-blog" -n "fm" -c "\~/myblog" && tmux new-window -t "write-blog" -n "edt" -c "\~/myblog" && tmux new-window -t "write-blog" -n "svr" -c "\~/myblog" && tmux attach -t "write-blog"
  3. 작성한 blog글을 publish하는 작업 수행:
    hugo --baseURL="https://willson.kr/" && rsync -avz --delete $HOME/myblog/public/ will@publish-server:/var/www/myblog

써놓고보니 너무 길지 않나? 맞다. 내 말이 그말이다! 우리는 반복하는 걸 싫어한다. 코딩을 할 때도 변수, 함수, 그리고 클래스 같은 걸 정의해두고 반복하여 사용한다.

그렇다면, 리눅스라고 이런 방법이 없겠는가? 당연히 있다.

대표적으로 다음의 두 가지 방식을 사용할 수 있다.

  1. shell의 alias 기능 사용하기
  2. 개인이 쓰는 쉘 스크립트 작성하기

이들만 잘 활용한다면 긴 명령어를 일일이 작성할 필요가 없어지고, 리눅스의 진가를 알게될 것이다. (더이상 윈도우로 돌아갈 수 없어진다.) 각 방식을 알아보기 전에, 우선 terminal, shell, 그리고 kernel의 관계를 명확히 하고 가자.

Terminal, shell 그리고 kernel의 관계

우리는 터미널 (e.g. kitty, alacritty, st, …)와 소통을 한다. 여기에 명령어를 입력하고 return하면 그걸 shell (e.g. bash, zsh, fish, …)이 해석해서 linux kernel을 통해 명령을 수행한다. 그리고 그 결과를 다시 shell이 terminal에 전달하면, terminal에 결과가 출력되어 우리가 그걸 확인하게 된다.

  graph LR
    A(("User"))
    B["Terminal"]
    C["Shell"]
    D["Linux<br>kernel"]

    A -->|명령어| B -->|명령어| C --> |해석된 명령어| D e1@-->|명령 수행| D
    D -->|결과 출력|C -->|결과 출력|B -->|결과 출력|A

    e1@{ animate: true }

Terminal과 shell은 현재 어떤 배포판을 사용하는지 그리고 사용자가 어떤 종류를 선택했는지에 따라, 서로다른 조합을 가질 수 있다. 그러나 그들이 하는 일은 위 도식과 완전히 일치한다.

다음으로, 내가 어떤 terminal과 shell을 사용하는지 확인해보자.

내가 사용하고있는 terminal과 shell을 확인하는 법

다음의 두 명령어는 각각 내가 사용하는 terminal과 shell의 정보를 담고있는 변수를 출력한다.

bash
echo $TERM
bash
echo $SHELL

필자는 각 명령어에 대해 다음과 같은 출력이 나왔다. 이들 결과는 kitty를 터미널로 사용하고 있고, bash를 shell로 사용하고 있다는 정보를 준다.

echo $TERM
xterm-kitty
echo $SHELL
/bin/bash

내가 사용하는 터미널과 쉘을 확인했으니, 본격적으로 각 방식에 대해 알아보자.

shell의 alias

alias의 예시

각 shell은 rc라는 설정 파일을 갖고 있다. bash는 $HOME/.bashrc이고, zsh는 $HOME/.zshrc이다. 여기엔 보통 이런 설정들이 담긴다.

  1. 터미널 prompt 커스터마이징
  2. alias로 명령어 약어 만들기
  3. sh의 특수한 기능 (e.g. vi 모드 사용) 설정

필자는 다음같은 $HOME/.bashrc 파일을 사용하고 있다.

# ~/.bashrc

# If not running interactively, don't do anything
[[ $- != *i* ]] && return

#==================================================#
# bash vi mode ON                                  #
#==================================================#
set -o vi

#==================================================#
# prompt setting                                   #
#==================================================#
ource /usr/share/git/completion/git-prompt.sh
PS1='\[\033[01;36m\]\u\[\033[00m\]@\[\033[01;32m\]\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\[\033[01;33m\]$(__git_ps1 " (%s)")\[\033[00m\]\$ '

#==================================================#
# alias things                                     #
#==================================================#
alias ls='ls --color=auto'
alias ll='ls -l --color=auto'
alias grep='grep --color=auto'
alias md='udisksctl mount -b'
alias ud='udisksctl unmount -b'

여기서 우리가 관심을 가지는 부분은 alias 부분이다. 필자의 alias에는 총 다섯 개 항목이 존재한다.

#==================================================#
# alias things                                     #
#==================================================#
alias ls='ls --color=auto'
alias ll='ls -l --color=auto'
alias grep='grep --color=auto'
alias md='udisksctl mount -b'
alias ud='udisksctl unmount -b'

이들 중 ls를 살펴보자. 이건 필자가 입력한 것은 아니고, os가 자동으로 설치과정에 입력한 것이다. bash에서 우리가 ls를 명령어를 입력하면, 이건 사실 ls --color=auto를 실행한다. 지금껏 한번도 .bashrc를 들여다본 적이 없다면 ls가 색상을 입히면서 출력하고 있단 것이 default인 줄 알았을 것이다.

차이를 확인하고 싶으면 터미널에 ls\ls를 각각 입력해보라. \s는 본래 ls 명령어를 수행해서 보여줄 것이고 그 차이가 한눈에 보일 것이다.

`ls`와 `\ls`의 차이
ls\ls의 차이

alias 설정하는 방법

앞서 봤던 예시들을 유심히보면 사용 방식을 이미 눈치챘을 지도 모르겠다. alias는 다음처럼 추가할 수 있다.

$HOME/.bashrc
alias <내가-정한-명령어>='<실제 수행할 명령어>'

이 방식으로 tmux로 블로그 환경을 만드는 명령어의 alias를 지정하자면 다음과 같다.

$HOME/.bashrc
alias tmux-blog='tmux new-session -d -s "write-blog" -n "fm" -c "\~/myblog" && tmux new-window -t "write-blog" -n "edt" -c "\~/myblog" && tmux new-window -t "write-blog" -n "svr" -c "\~/myblog" && tmux attach -t "write-blog"'

자, 이렇게 원하는 alias 항목들을 추가해주고 저장해주자.

그러면 다음 터미널 세션을 실행할 때부터 tmux-blog만 써서 tmux new-session -d -s "write-blog" -n "fm" -c "\~/myblog" && tmux new-window -t "write-blog" -n "edt" -c "\~/myblog" && tmux new-window -t "write-blog" -n "svr" -c "\~/myblog" && tmux attach -t "write-blog"와 같이 긴 명령어를 대체할 수 있게 된다.

혹시 터미널을 닫지 않은 상태에서 확인을 해보고 싶다면, 다음의 명령어로 새로 저장한 .bashrc 파일을 source 해주고 나의 명령어를 실행해보자.

bash
source $HOME/.bashrc
tmux-blog

그러면 그 터미널에서 바로 tmux-blog와 같은 나의 명령어를 실행해볼 수 있을 것이다.

shell scripting

shell alias만으로 부족합니까?

우린 방금 shell의 rc파일의 alias 기능을 사용하여 긴 명령어에 별명을 붙여주는 방식을 배웠다. 그럼, 이것만으로 충분한 것 아닌가라는 생각이 들 것이다.

맞다. 단순한 작업들은 alias 기능만으로 충분하다. 내가 말하는 단순한 작업이란, 별도 사용자의 입력없이 항상 똑같이 입력하면 되는 명령어를 의미한다.

그런데 생각해보자. 아까 만들었던 이 alias를 보자. 여기선, tmux session 이름을 write-blog로, 그리고 각 창의 이름을 fm, edt, 그리고 svr고정하였다.

$HOME/.bashrc
alias tmux-blog='tmux new-session -d -s "write-blog" -n "fm" -c "\~/myblog" && tmux new-window -t "write-blog" -n "edt" -c "\~/myblog" && tmux new-window -t "write-blog" -n "svr" -c "\~/myblog" && tmux attach -t "write-blog"'

그런데 이번에 나는 write-article로, 그리고 각 창의 이름을 fm, edt, preview로 만들고 싶다면? 새로운 alias를 또 만들까?

우리는 이때 생각하게 된다.

아!.. 반복 작업이다. 좀 짜증이 나기 시작하는 걸?

그렇다. 이처럼 모든 경우에 대해 alias를 다 만들어둬야만 원하는 결과를 얻을 수 있다면, 그건 그것대로 귀찮다. 이럴 때 필요한 것이 shell scripting이다.

개인 sciript를 저장할 directory 만들기

우리는 앞으로 점점 더 많은 스크립트를 만들고 사용하게 될 것이다. 그러니 이들을 담아둘 경로를 하나 지정해두는 편이 좋다.

나는 개인적으로 $HOME/.local/bin을 스크립트 전용 폴더로 사용하고 있다. 본인이 원하는 다른 경로로 설정해도 아무 상관이 없다.

bash
mkdir -p $HOME/.local/bin # 원하는 폴더를 하나 만들어주자.

$PATH에 개인 script directory 추가하기

우리가 만든 폴더의 위치는 우리는 알고 있다. 그러나, shell은 이걸 모른다. 그러니까 이걸 알 수 있도록 PATH 변수에 추가해줄까 한다.

LaTeX\LaTeX 포스팅의 환경변수 설정에서 환경변수를 추가했던 것과 같이, 환경변수에 $HOME/.local/bin 경로를 추가해주자. 자세한 환경변수 설정 방법은 여기 를 참고하자.

잘 추가되었다면, 터미널에서 다음의 명령어를 실행했을 때 우리 경로가 거기에 포함되어 있어야 한다.

bash
echo $PATH
# /home/will/.local/bin이 포함되어 있는 걸 알 수 있다.
/home/will/.cargo/env:/home/will/.local/bin:/usr/local/texlive/2024/bin/x86_64-linux:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl

스크립트 작성하기

자, 이제 준비는 끝났다. 본격적으로 shell script를 만들어보자.

나는 zettelkasten을 할 때 수시로 필요한, 모든 변화들을 commit 하고 push하는 스크립트를 짜봤다. 이때 Commit message는 입력으로 받되, 입력이 없을 땐 default로 sync로 입력하게 했다. 그리고 변경사항이 없다면 아무것도 하지 않도록 했다.

$HOME/.local/bin/zetsync
#!/bin/bash
set -euo pipefail
cd ~/zettelkasten/ || exit 1

# 커밋 메시지를 인자로 받음 (없으면 기본 sync)
COMMIT_MSG=${1:-"sync"}

# 변경사항(신규/수정/삭제/스테이징 포함) 확인
# --porcelain은 스크립트용 안정된 출력, 기본값으로 untracked도 포함
if [ -z "$(git status --porcelain=v1)" ]; then
    echo "No changes to commit."
    exit 0
fi

# 변경사항 있으면 add/commit/push 진행
git add -A
git commit -m "$COMMIT_MSG" || {
  echo "Nothing to commit after add (race or ignore rules?)"
  exit 0
}
git push

필자도 shell scripting에 서툴지만, 몇 가지 포인트를 알려주자면 다음과 같다.

  • 시작은 #!/bin/bash로 한다.
  • shell script의 확장자 .sh를 꼭 붙일 필요는 없다. (e.g. zetsync)
  • shell script는 반드시 executable 권한을 부여해줘야 한다.

개인 스크립트를 작성했다면 실행 권한을 부여해주자.

bash
chmod +x $HOME/.local/bin/zetsync

이로써 우리는 shell alias와 달리, 많은 variation에 대해서도 동작하는 zetsync란 명령어를 갖게 되었다.

스크립트 사용하기

이제 터미널에서 다음처럼 많은 variation에 대해서도 원하는대로 동작하는 지 사용해보자.

bash
zetsync 'first sync'
zetsync 'second sync'
zetsync 'third sync'
zetsync 'fourth sync'

잘 안된다면, 환경변수 설정이 틀렸거나 스크립트에 문제가 있는 것이다. 찬찬히 다시 해결해보자.

마무리하며

오늘은 자주 사용하는 긴 명령어들을 간단하게 사용하는 두 가지 방식에 대해 알아보았다. 이걸 잘 익혀서 사용한다면, 리눅스 생활이 더욱 윤택해질 것이다.

Last updated on