dotfiles를 10여 년 관리하며 만든 작은 셸 스크립트들 가운데, 내가 즐겨 쓰는 것들을 한데 모았습니다.
내가 dotfiles를 10여 년 넘게 관리해 오면서, 자잘한 셸 스크립트를 정말 많이 만들었다. 여기에 내가 특히 좋아하는 것들의 큰 목록을 소개한다.
copy와 pasta는 macOS의 pbcopy, Linux의 xclip처럼 시스템 클립보드 관리자를 감싼 간단한 래퍼다. 이 둘은 맨날 쓴다.
# 상위 수준 예시
run_some_command | copy
pasta > file_from_my_clipboard.txt
# 파일 내용 복사하기
copy < file.txt
# 클립보드의 파일 경로 열기
vim "$(pasta)"
# 클립보드의 base64 디코드
pasta | base64 --decode
pastas는 현재 클립보드 상태를 stdout에 출력하고, 이후 클립보드가 바뀔 때마다 새 내용을 출력한다. 일주일에 한 번 정도 쓴다.
# 상위 수준 예시
pastas > everything_i_copied.txt
# 클립보드에 복사하는 모든 링크를 다운로드
pastas | wget -i -
cpwd는 현재 디렉터리를 클립보드로 복사한다. 사실상 pwd | copy. 한 터미널 탭에서 디렉터리에 들어와 있고, 그 디렉터리를 다른 탭에서 쓰고 싶을 때 자주 쓴다. 한 탭에서 복사하고, 다른 탭에서 cd한다. 하루에 한 번꼴로 쓴다.
mkcd foo는 디렉터리를 만들고 그 안으로 cd한다. 사실상 mkdir foo && cd foo. 이건 맨날 쓴다—디렉터리를 만들 때는 거의 항상 그 안으로 들어가고 싶기 때문이다.
tempe는 임시 디렉터리로 이동한다. 사실상 cd "$(mktemp -d)". 샌드박스 디렉터리로 툭 들어갔다 나오고 싶을 때 맨날 쓴다. 작업을 수동으로 정리하지 않아도 된다. 몇 가지 흔한 예시는 다음과 같다:
# 파일 다운로드 후 압축 해제
tempe
wget 'https://example.com/big_file.tar.xz'
tar -xf big_file.tar.xz
# ...파일로 뭔가를 한다...
# 간단히 버릴 스크립트를 써서 테스트해 보기
tempe
vim foo.py
python3 foo.py
trash a.txt b.png는 a.txt와 b.png를 휴지통으로 보낸다. macOS와 Linux 둘 다 지원한다. 매일 쓴다. rm보다 확실히 더 자주 쓰고, 덕분에 파일을 실수로 지우는 일을 막아 준다.
mksh는 셸 스크립트를 빠르게 만들 수 있게 해 준다. mksh foo.sh는 foo.sh를 만들고, chmod u+x로 실행 가능하게 만들고, 보기 좋은 Bash 프리앰블을 추가하고, 내 에디터(내 경우 Vim)로 연다. 며칠에 한 번꼴로 쓴다. 이 글에 나온 스크립트 중 많은 것들이 이 도우미로 만들어졌다!
serveit는 현재 디렉터리에서 localhost:8000으로 정적 파일 서버를 시작한다. 사실상 python3 -m http.server 8000인데, Python이 설치되어 있지 않은 경우를 처리해 다른 프로그램으로 폴백한다. 일주일에 몇 번 쓴다. 웹 개발자가 아니라면 덜 유용할 수 있다.
getsong는 yt-dlp를 사용해, 주로 YouTube나 SoundCloud에서 가능한 최고 음질로 노래를 다운로드한다. 예를 들어 getsong https://www.youtube.com/watch?v=dQw4w9WgXcQ는 해당 영상을 노래로 다운로드한다. 일주일에 몇 번 쓴다… 대개 게임 사운드트랙을 챙길 때…
getpod도 비슷하게 yt-dlp로 팟캐스트 플레이어에 적합한 형식으로 다운로드한다. 소리만 듣고 싶은 영상이 꽤 있다. 한 달에 몇 번 쓴다.
getsubs는 영상의 영어 자막을 다운로드한다. (가능하면 “공식” 자막을 찾고, 실패하면 자동 생성 자막으로 폴백하는 약간의 요령이 있다.) 때로는 자막을 직접 읽고, 때로는 getsubs https://video.example/foo | ollama run llama3.2 "Summarize this"처럼 요약시키기도 하고, 가끔은 영상을 컴퓨터에 저장하고 싶지 않을 때 백업용으로 쓴다. 며칠에 한 번 쓴다.
wifi off, wifi on, wifi toggle은 시스템 Wi‑Fi를 제어할 때 유용하다. 네트워크에 문제가 있을 때는 wifi toggle을 가장 자주 쓴다. 한 달에 한 번 정도.
url "$my_url"은 URL을 구성 요소로 파싱한다. 추적 링크처럼 보기 싫은 걸 클릭하지 않고 URL에서 데이터만 뽑고 싶을 때 한 달에 한 번 정도 쓴다.
url 'https://evil.example/track-user-link?url=https%3A%2F%2Furl-i-want-to-visit.example&track=06f8582a-91e6-4c9c-bf8e-516884584aba#cookie=123'
# 원본: https://evil.example/track-user-link?url=https%3A%2F%2Furl-i-want-to-visit.example&track=06f8582a-91e6-4c9c-bf8e-516884584aba#cookie=123
# 프로토콜: https
# 호스트명: evil.example
# 경로: /track-user-link
# 쿼리: url=https%3A%2F%2Furl-i-want-to-visit.example&track=06f8582a-91e6-4c9c-bf8e-516884584aba
# - url https://url-i-want-to-visit.example
# - track 06f8582a-91e6-4c9c-bf8e-516884584aba
# 해시: cookie=123
line 10은 stdin에서 10번째 줄을 출력한다. 예를 들어 cat some_big_file | line 10은 파일의 10번째 줄을 출력한다. head나 tail처럼 기본으로 있어야 할 것 같은 기능이다. 한 달에 한 번 정도 쓴다.
scratch는 임시 Vim 버퍼를 연다. 사실상 $EDITOR $(mktemp)의 별칭이다. 간단한 텍스트 조작 작업이나, 금방 버릴 메모를 위해 하루에 한 번 정도 쓴다.
straightquote는 “스마트 따옴표”를 “일반 따옴표”(“덤 쿼트”라고도 함)로 바꾼다. 평소엔 크게 신경 쓰지 않지만, 가끔 작업 중인 코드에 슬며시 들어오기도 한다. 파일 크기를 줄여 주기도 해서, 가끔 유용하다. 일주일에 한 번 이상은 쓴다.
markdownquote는 모든 줄 앞에 >를 붙인다. Vim에서 자주 쓰는데, 영역을 선택한 뒤 :'<,'>!markdownquote를 실행해 선택 영역을 인용한다. 일주일에 한 번 정도.
length foo는 3을 반환한다. (아마 그냥 wc -c를 썼어야 했을 것이다.)
jsonformat는 stdin의 JSON을 받아 보기 좋게 들여써서 stdout으로 출력한다. 1년에 몇 번 쓴다.
uppered와 lowered는 문자열을 각각 대문자와 소문자로 바꾼다. 예를 들어 echo foo | uppered는 FOO를 반환한다. 일주일에 한 번쯤 쓴다.
nato bar는 Bravo Alfa Romeo를 반환한다. 고객센터와 통화할 때 긴 영숫자 문자열을 읽어 줘야 하는 상황—내 인생 통틀어 몇 번 없었지만—에 가장 자주 쓴다. 그래도 가끔 유용하다!
u+ 2025는 ñ, LATIN SMALL LETTER N WITH TILDE를 반환한다. 유니코드 문자열을 빠르게 조회할 수 있다. 그렇게 자주 쓰지는 않는다… 아마 한 달에 한 번 정도.
snippets foo는 ~/.config/evanhahn-snippets/foo를 cat한다. 나는 snippet arrow로 →, snippet recruiter로 구인 담당자에게 보내는 짤막한 “관심 없음” 답변, snippet lorem으로 “Lorem ipsum” 블록을 출력하는 등 몇 가지를 쓴다. 일주일에 한두 번쯤은 이 중 하나를 쓴다.
Ruby에 내장된 irb REPL에서 영감을 받아 다음을 만들었다:
iclj: Clojure REPL 시작ijs: Deno REPL 시작(Deno가 없으면 Node REPL)iphp: PHP REPL 시작ipy: Python REPL 시작isql: SQLite 셸 시작(sqlite3 :memory:의 별칭)hoy는 현재 날짜를 2020-04-20 같은 ISO 형식으로 출력한다. 파일 이름 앞에 현재 날짜를 붙이는 걸 좋아해서 맨날 쓴다.
timer 10m는 10분 타이머를 시작하고, (1) 벨 소리를 재생하고 (2) OS 알림을 보낸다(아래 notify 참고). 배경에서 5분 타이머를 시작하려고 종종 bb timer 5m을 쓴다(아래 bb 참고). 시간을 관리하는 데 거의 매일 쓴다.
rn는 date와 cal을 사용해 현재 시각과 날짜를 출력한다. 아마 일주일에 한 번 정도 쓴다. 출력 예시는 다음과 같다:
4:20PM on Wednesday, October 22, 2025
September 2025
Su Mo Tu We Th Fr Sa
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
ocr my_image.png는 이미지에서 텍스트를 추출해 stdout으로 출력한다. 안타깝게도 현재는 macOS에서만 동작하지만, 고치고 싶다. (이 스크립트에 관한 글도 썼다.)
boop은(셸 스크립트가 아닌 별칭) 이전 명령이 성공했으면 기쁜 소리를, 실패했으면 슬픈 소리를 낸다. run_the_tests ; boop처럼 쓰면, 테스트가 성공했는지 실패했는지를 소리로 알 수 있다. 오래 걸리는 명령에도 유용한데, 끝났을 때 작은 알림음을 들을 수 있기 때문이다. 이건 맨날 쓴다.
sfx foo는 기본적으로 ~/.config/evanhahn-sfx/foo.ogg를 재생한다. 위의 boop과 timer에서 사용된다.
tunes는 mpv로 파일에서 오디오를 재생한다. 이건 맨날 쓴다. 보통 tunes --shuffle ~/music.
pix는 mpv로 사진을 보여 준다. 사진을 볼 때 일주일에 몇 번 쓴다.
radio는 좋아하는 인터넷 라디오 스테이션을 감싼 작은 래퍼다. radio lofi와 radio salsa를 특히 좋아한다. 한 달에 몇 번 쓴다.
speak는 stdin을 읽고 모든 Markdown 서식을 제거한 뒤, 텍스트-음성 변환 시스템(macOS의 say, Linux의 espeak-ng)으로 보낸다. 소리 내어 교정할 수 없을 때 TTS를 쓰는 걸 좋아한다. 한 달에 몇 번 쓴다.
shrinkvid는 영상을 조금 압축하는 ffmpeg 래퍼다. 한 달에 한 번쯤 쓴다.
removeexif는 JPEG에서 EXIF 데이터를 제거한다. PNG 같은 다른 포맷의 EXIF는 제거하지 못해서 자주 쓰진 않지만… 언젠가 확장하고 싶어서 계속 들고 다닌다.
tuivid는 거의 쓰지 않지만, 터미널에서 영상을 볼 수 있다. 약간 괴랄하지만, 안 쓰더라도 사랑한다.
each는 내가 쓰기 어렵게 느끼는 xargs와 find ... -exec에 대한 내 해답이다. 예를 들어 ls | each 'du -h {}'는 디렉터리의 모든 파일에 du -h를 실행한다. 자주는 아니지만, xargs를 매번 틀리는 나에겐 좋은 대안이다.
running foo는 ps aux | grep foo와 비슷하지만(내 기준) 훨씬 읽기 쉽다—PID(보라색으로 하이라이트)와 명령만 보여 준다.
murder foo 또는 murder 1234는 kill을 감싼 래퍼로, 먼저 kill -15 $PID를 보내고 잠깐 기다린 뒤 kill -2, 다시 기다리고 kill -1, 다시 기다린 후 마지막으로 kill -9를 보낸다. 프로그램을 멈추고 싶을 때는, 과격해지기 전에 정중하게 먼저 부탁하고 싶다. 한 달에 몇 번 쓴다.
waitfor $PID는 계속하기 전에 해당 PID가 종료될 때까지 기다린다. 또한 시스템이 절전 모드로 들어가지 않게 막아 준다. 한 달에 한 번 정도, 다음과 같은 일을 할 때 쓴다:
# 다른 프로세스가 끝난 뒤에만 무언가를 시작하고 싶다
waitfor 1234 ; something_else
# 오래 걸리는 프로세스를 시작했는데, 끝나면 알림을 받고 싶다
waitfor 1234 ; notify 'process 1234 is done'
bb my_command는 my_command &와 비슷하지만, 진짜로 완전히 백그라운드에서 실행한다. 그 프로그램으로부터 다시는 출력이 오지 않는다. 데몬이나 오래 도는 프로세스를, 정말 신경 쓰지 않고 시작하고 싶을 때 유용하다. 나는 주로 bb ollama serve와 bb timer 5m를 쓴다. 하루에 한 번쯤 쓴다.
prettypath는 $PATH를 각 항목이 줄바꿈으로 구분되도록 출력해서 읽기 쉽게 만들어 준다. $PATH 문제를 디버깅할 때—드문 일이지만—가끔 쓰고, 그럴 때 정말 고맙다.
tryna my_command는 my_command가 성공할 때까지 계속 실행한다. trynafail my_command는 실패할 때까지 실행한다. 많이 쓰진 않지만 여러모로 유용하다. tryna wget ...는 다운로드가 성공할 때까지 계속 시도한다. trynafail npm test는 테스트가 실패하기 시작하면 멈춘다.
emoji는 이모지 조회 도우미다. 예를 들어 emoji cool은 다음을 출력한다:
😛
😒
😎
🪭
🆒
httpstatus는 모든 HTTP 상태 코드를 출력한다. httpstatus 204는 204 No Content를 출력한다. 웹 개발자로서, 온라인에서 찾는 대신 한 달에 몇 번 쓴다.
alphabet은 그냥 영어 알파벳을 소문자와 대문자로 출력한다. 놀랍게도 꽤 자주(아마 한 달에 한 번 정도) 쓴다. 실제로 딱 다음을 출력한다:
abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
theme 0은 시스템 전체를 다크 모드로 바꾼다. theme 1은 라이트 모드로 바꾼다. OS 테마만 바꾸는 것이 아니라, Vim, Tmux, 터미널 테마까지 바꾼다. 하루에 한 번 이상은 쓴다.
sleepybear는 시스템을 잠자기 모드로 전환하며, macOS와 Linux에서 모두 동작한다. 일주일에 몇 번 쓴다.
ds-destroy는 디렉터리 내의 .DS_Store 파일을 재귀적으로 모두 삭제한다. macOS가 이런 파일로 디렉터리를 어지럽히는 게 정말 싫다! 자주 쓰지는 않지만, 필요할 때 있어서 다행이다.
catbin foo는 사실상 cat "$(which foo)"다. PATH에 있는 파일의 소스 코드를 확인할 때 유용하다(예를 들어 이 글을 쓰면서 사용했다!). 한 달에 몇 번 쓴다.
notify는 OS 알림을 보낸다. 위의 여러 스크립트에서 사용했다(위 참고). 또한 한 달에 한 번 정도는 다음처럼 쓰기도 한다:
run_some_long_running_process ; notify 'all done'
uuid는 v4 UUID를 출력한다. 한 달에 한 번 정도 쓴다.
여기 소개한 건 내가 자주 쓰는 스크립트들뿐이다. 당신에게도 몇 가지라도 유용했으면 한다!
이 글이 마음에 들었다면, “왜 alias는 내게 최후의 수단인가”와 “A decade of dotfiles”도 흥미로울 것이다.
아, 그리고 내가 좋아할 만한 스크립트가 있다면 연락해 달라.