여러분의 CS 교육에서 누락된 학기 - 셸 스크립팅 2부

Hyuno Choi·2021년 7월 11일
0
post-thumbnail

이 포스팅은 MIT 공개 강의를 바탕으로 작성되었습니다. https://missing.csail.mit.edu/


2021년 7월 11일

지난 포스팅에서 쉘 스크립팅 문법에 대해 중점적으로 알아보았다면, 이번 포스팅에서는 쉘에서 파일이나 텍스트를 탐색할 때 수고를 덜어줄 수 있는 방법에 대해 배워보겠습니다.

셸 globbing

와일드 카드

와일드 카드는 ?, *로 두 종류입니다. 이름에서 알 수 있듯이 원하는 문자를 일치시킬 수 있습니다. ?가 차지하는 자리에는 모든 문자가 들어갈 수 있습니다.

예를 들어, test1test2라는 두 파일을 동시에 삭제하기 위해서는 rm test?이라고 입력해주면 됩니다.

*은 길이와 상관 없이 모든 문자가 들어갈 수 있습니다. 방금 전의 명령어인 rm test?test9는 삭제할 수 있지만 test10은 삭제할 수 없습니다. 그러나 rm test*이라고 입력하면 test로 시작하는 모든 파일을 제거합니다.

중괄호

중괄호를 사용하면 중복되는 파일명을 여러번 쓰지 않아도 됩니다. 예를 들어, test1 부터 test10까지 파일을 만든다고 가정하면, test라는 글자를 총 10번 입력해야 합니다. 이럴 때 중괄호가 반복되는 작업의 양을 줄여줄 수 있습니다.

bash-5.1$ touch test{1..10}
bash-5.1$ ls
test1	test10	test2	test3	test4	test5	test6	test7	test8	test9

간단하게 1부터 10까지의 test 파일을 만들었습니다.

bash-5.1$ touch test.{py,js}
bash-5.1$ ls
test.js	test.py

중괄호를 사용할 때는 범위를 넣어줄 수도 있고 쉼표로 구분된 여러 값을 넣어줄 수도 있습니다.

와일드 카드와 중괄호를 함께 사용하면 여러 파일명을 인자로 적거나 특정 파일을 찾는 과정을 단축할 수 있습니다. 예를 들어, rm *.{txt, py} 명령을 사용하면 현재 디렉토리에 있는 모든 txtpy 파일을 삭제할 수 있습니다.

셸 스크립트 오류 찾기

셸 스크립트에 오류가 있으면 셸에서 오류 메시지를 출력합니다.

bash-5.1$ ./test.sh
./test.sh: 줄 4: 예기치 않은 토큰 'da' 근처에서 문법 오류
./test.sh: 줄 4: `da'

bash 반복문에 쓰이는 do를 일부러 da 로 바꿨습니다. 오류가 발생한 부분을 짚어주기는 하지만 다른 프로그래밍 언어의 오류메시지에 비하면 정보가 빈약한 편입니다.

기본 오류메시지의 단점을 보완하기 위하여 셸 스크립트 오류에 대한 자세한 정보를 출력해주는 프로그램을 다운받을 수 있습니다. shellcheck 가 그중 하나입니다. 저는 macOS 환경이므로 $ brew install shellcheck 명령어를 사용해 설치했습니다. 각 개발환경 별 설치 방법은 깃험 페이지를 참고해주세요. https://github.com/koalaman/shellcheck

bash-5.1$ shellcheck test.sh

In test.sh line 3:
for i in $@
^-- SC1073: Couldn't parse this for loop. Fix to allow more checks.


In test.sh line 4:
da
^-- SC1058: Expected 'do'.
 ^-- SC1072:  Fix any mentioned problems and try again.

For more information:
  https://www.shellcheck.net/wiki/SC1058 -- Expected 'do'.
  https://www.shellcheck.net/wiki/SC1072 --  Fix any mentioned problems and t...
  https://www.shellcheck.net/wiki/SC1073 -- Couldn't parse this for loop. Fix...

아까와 똑같은 스크립트 파일을 shellcheck 명령어에 인자로 전달했습니다. bash 기본 오류 메시지보다 훨씬 많은 정보를 보여줍니다.

  1. 반복문을 실행할 수 없다는 것과
  2. da 가 들어간 자리에 do 가 들어가야 한다는 것까지 알려줍니다.

env로 shebang 작성하기

지금까지 shebang 을 작성할 때 해당 스크립트를 실행할 인터프리터를 해당 인터프리터의 절대경로로 설정해주었습니다. 예를 들어 #!/bin/bash 와 같은 식입니다. 이 shebagenv 를 활용하여 작성할 수도 있습니다.

#!/usr/bin/env bash

이렇게 작성하면 환경변수에 저장된 bash 프로그램의 경로를 통해 bash 인터프리터로 스크립트가 실행됩니다. 만약 절대경로로 shebag을 작성한 프로그램을 다른 사람에게 보내줬는데 그 사람이 나와 다른 경로에 인터프리터를 설치했다면 프로그램을 실행할 수 없을 것입니다. env 를 사용해 shebang 을 작성하면 다른 컴퓨터의 인터프리터 설치 경로를 신경쓰지 않고 스크립트를 작성할 수 있습니다.

Shell Tools

명령어 사용 방법 찾기

특정 명령어의 사용법이나 사용할 수 있는 옵션의 종류가 궁금할 때 정보를 얻을 수 있는 방법에 대해 알아보겠습니다.

-h 또는 --help 활용

프로그램마다 다르긴 하지만, 대부분의 프로그램에서 프로그램명이나 커맨드와 함께 -h 또는 --help 플래그를 사용하면 해당 명령을 사용하는 방법을 출력해줍니다. 예를 들어, $ git --help 라고 입력하면 다음과 같은 도움말을 출력해줍니다.

man 명령 사용

manmanual의 약자입니다. $ man [프로그램명] 명령어를 사용하면 해당 프로그램에 대한 상세한 정보를 출력해줍니다. $ man git 이라고 입력하면 git에 대한 자세한 정보를 알 수 있습니다.

--help 플래그나 man 명령을 사용할 때 파이프 라인을 마치 검색 기능처럼 활용할 수 있습니다. 예를 들어, 깃 도움말에서 커밋과 관련된 정보만 얻고 싶다면 다음과 같이 명령어를 입력하면 됩니다.

$ git --help | grep commit

지난번에 배운 내용을 응용한 것입니다. 앞 명령어 출력에서 grep 명령을 통헤 commit 을 포함하는 줄만 골라 출력합니다. 커밋과 관련된 내용만 깔끔하게 출력되었습니다.

tldr

-h 플래그나 man 명령이 프로그램에 대한 자세한 설명을 보여주기는 하지만 불필요한 정보가 많고 사용하기 불편하기도 합니다. tldr 는 명령어와 관련된 정보를 훨씬 깔끔하고 간결하게 보여주는 프로그램입니다. tldrnpm 모듈이므로 npm을 이용해 설치합니다.전역으로 사용할 것이므로 -g 옵션을 사용합니다.

$ sudo npm i -g tldr

이제 명령어에 대한 자세한 설명을 알고 싶으면 $ tldr <명령어> 처럼 사용할 수 있습니다. 예시로 $ tldr ls 라고 입력하면 위의 두 방법보다 훨씬 깔끔하게 정보가 출력됩니다.

npm 을 통해 설치하는 대신에 tldr 사이트에서 명령어를 검색할 수도 있습니다. https://tldr.sh

파일 찾기

굳이 프로그래밍을 할 때가 아니더라도 대량의 파일을 찾거나 수정할 일은 많습니다. 예를 들어 .jpg 사진과 .png 사진이 섞여 있는 폴더에서 .jpg 사진만 골라내어 모두 .png 로 변환해야 하는 경우가 있을 수 있습니다. 이런 반복 작업을 할 때는 GUI 보다 커맨드라인 인터페이스가 훨씬 유리합니다.

find로 파일 찾기

find는 리눅스 시스템에서 디렉터리나 파일을 찾을 수 있는 명령어입니다. 또한 찾은 파일을 가지고 실행할 명령을 내릴 수도 있습니다. CS 강의 원문의 설명이 빈약한 관계로 find 명령어의 사용법을 잘 정리해놓은 사이트의 글을 참고하여 정리했습니다. https://linuxize.com/post/how-to-find-files-in-linux-using-the-command-line

기본 사용법

find 명령어의 기본 사용법은 다음과 같습니다.

find [options] [path...] [expression]
  • options: 심볼릭 링크, 디버깅 옵션, 최적화 방법을 지정합니다.
  • path: 탐색을 시작할 디렉토리를 지정합니다.
  • expression: 옵션, 탐색 패턴, 수행할 동작을 지정합니다.

이름으로 파일 찾기

아마 find 명령어를 가장 많이 사용하는 방법이 이름으로 파일 찾기일 것입니다. 이름으로 파일을 찾을 때는 -name 옵션을 사용합니다.

예를 들어 홈 디렉토리에서 ./Desktop/test 경로에 있는 image.png 파일을 찾는다고 한다면 find 명령어를 다음과 같이 쓸 수 있습니다.

$ find ./Desktop/test -type f -name image.png

-type 은 찾으려는 대상의 타입을 지정해주는 옵션으로, 이미지 파일이므로 f 를 사용합니다. -type 옵션에 대해서는 밑에서 자세히 다루겠습니다.

만약 이름으로 파일을 찾을 때 대소문자 구분을 하지 않으려면 -name 대신 -iname 을 사용합니다. i 는 insensitive를 의미합니다.

$ find ./Desktop/test -type f -iname IMAGE.png

-iname 을 사용하면 파일명을 대문자로 쓰더라도 위의 명령어와 동일한 파일을 찾을 수 있습니다.

확장자로 파일 찾기

find 명령어로 특정 확장자를 가진 파일을 모두 찾을 수 있습니다. 예를 들어 위에서 예시로 든 .jpg 확장자의 이미지 파일만 찾는 명령을 내릴 수 있습니다. 홈 디렉토리에서 ./Desktop 경로에 있는 모든 .jpg 파일을 찾는 명령은 다음과 같이 쓸 수 있습니다.

$ find ./Desktop -type f -name '*.jpg'

앞에서는 파일 이름을 그냥 인자로 전달했는데 이번에는 '' 를 사용해 파일명을 감싸주었습니다. 이는 쉘 인터프리터에게 * 을 와일드카드가 아니라 패턴의 일부로 사용했다는 것을 알려주기 위해서입니다. '' 로 파일명을 묶지 않으려면 * 앞에 \ 탈출 문자를 붙이면 됩니다.

$ find ./Desktop -type f -name \*.jpg

확장자 파일 찾기를 역으로 수행할 수도 있습니다. 예를 들어 .jpg 로 끝나지 않는 파일을 찾고 싶다고 한다면 단순히 -name 옵션 앞에 -not 옵션을 쓰면 됩니다.

$ find ./Desktop -type f -not -name '*.jpg'

타입으로 파일 찾기

지금까지 계속 사용한 -type 옵션을 단독으로 사용하면 특정 타입의 파일을 모두 찾을 수 있습니다. -type 옵션으로 찾을 수 있는 타입의 종류는 다음과 같습니다. 생소한 타입의 파일은 별도의 주석으로 작성했습니다.

  • f: 일반적인 파일
  • d: 디렉토리
  • l: 심볼릭 링크*
  • c: 캐릭터 디바이스*
  • b: 블록 디바이스*
  • p: 명명된 파이프*
  • s: 소켓*

  • 심볼릭 링크: 윈도우의 '바로가기' 같은 개념의 파일입니다. 특정 파일이 위치하는 경로까지 가지 않더라도 해당 파일에 접근할 수 있게 해줍니다.
  • 캐릭터 디바이스 & 블록 디바이스: 리눅스에서는 모든 장치를 파일로 관리합니다. 즉, 특정 장치를 의미하는 파일이라고 할 수 있습니다.
  • 캐릭터 디바이스: 키보드, 마우스, 모니터 등의 장치가 해당됩니다. I/O 전송 속도가 상대적으로 느립니다.
  • 블록 디바이스: 하드디스크, CD/DVD 등이 해당됩니다. 전송 속도가 높습니다.
  • 명명된 파이프: 프로세스들끼리 통신하는 방법 중 하나입니다. 일반 파이프는 사용하는 프로세스가 실행될 때에만 존재하나 명명된 파이프는 프로세스가 소멸해도 파일처럼 존재합니다.
  • 소켓: 네트워크를 통한 기기 간 통신에 사용됩니다. 리눅스에서는 네트워크 연결 역시 파일로 관리됩니다.

주석이 길어졌는데요, 일상적인 용도 안에서는 fd 를 가장 많이 쓰지 않을까 싶습니다. 현재 디렉토리에서 모든 디렉토리 파일을 찾는 명령은 다음과 같습니다.

$ find . -type d

용량으로 파일 찾기

-size 옵션을 사용하여 용량을 기준으로 파일을 탐색할 수 있습니다. 예를 들어 디스크 용량이 부족하여 정해진 용량을 넘는 비디오들만 골라 압축하려고 할 수 있습니다. -size 옵션에서 쓸 수 있는 용량의 단위는 다음과 같습니다.

  • b: 512 바이트 블럭
  • c: 바이트
  • w: two-byte words
  • k: 킬로바이스
  • M: 메가바이트(대문자)
  • G: 기가바이트(대문자)

홈 디렉토리에서 ./Desktop/test 경로에 있는 사이즈가 1024 바이트인 파일을 찾는 명령어는 다음과 같습니다.

$ find ./Desktop/test -type f -size 1024c

용량을 기준으로 파일을 검색할 때 보통 정확한 용량을 사용하진 않습니다. 그보다는 일정 용량 이상이나 이하의 파일을 찾을 것입니다. -size 옵션의 용량 앞에 - 를 붙이면 그 용량 미만, +를 붙이면 그 용량을 초과하는 파일을 찾을 수 있습니다. 위의 명령어를 1024 바이트를 초과하는 모든 파일을 찾는 것으로 바꾼다면 다음과 같이 쓸 수 있습니다.

$ find ./Desktop/test -type f -size +1024c

반대로 1024 바이트 미만의 모든 파일을 찾을 수도 있습니다.

$ find ./Desktop/test -type f -size -1024c

또한 -size 옵션을 사용할 때는 용량의 범위를 지정해줄 수도 있습니다. ./Desktop/test 경로에서 1M 초과, 10M 미만의 파일을 다음과 같이 찾을 수 있습니다.

$ find ./Desktop/test -type f -size +1M -size -10M 

수정일자로 파일 찾기

파일이 수정된 일자를 기준으로 파일을 찾는 것도 가능합니다. 수정 일자 기준 탐색은 -mtime 옵션을 사용합니다. 일자 기준 탐색 역시 파일 용량에서와 마찬가지로 -+를 사용합니다. 현재 디렉토리에서 수정한지 하루 미만인 파일은 다음과 같이 찾을 수 있습니다.

$ find . -type f -mtime -1

혹은, 건드리지 않은 지 10일이 넘어가는 파일은 다음과 같이 찾을 수 있습니다.

$ find . -type f -mtime +10

권한으로 파일 찾기

-perm 옵션으로 파일 접근 권한에 따라 파일을 찾을 수도 있습니다. 리눅스 시스템의 파일 권한에 대해서는 지난 포스팅에서 작성한 적이 있습니다. https://han.gl/4SGFC

$ ls -l 명령어를 입력하면 현재 작업 디렉토리에 있는 파일들의 상세한 정보를 보여주는데, 그 중에 제일 앞에 있는 10자리 문자열이 파일 권한에 해당합니다.

image.png 파일을 예로 들어보겠습니다.

  • 첫 번째 자리 - : 파일 종류를 표시합니다. <타입으로 파일 찾기> 에서 정리한 타입별 문자와 같으나 일반 파일의 경우 f 가 아니라 - 로 표시합니다.
  • 두 번째 ~ 네 번째 자리 rwx : 소유자 접근 권한을 의미합니다. r 은 읽기, w 는 쓰기, x 는 실행 권한을 의미합니다. 권한이 없는 곳은 - 로 표시됩니다.
  • 다섯 번째 ~ 일곱 번째 rwx : 그룹 접근 권한을 의미합니다.
  • 마지막 rwx: 기타 사용자 접근 권한을 의미합니다.

이상을 바탕으로 위의 스크린샷의 두 파일을 분석해보면, 두 파일 모두 일반 파일(-)이고, image.png 파일의 경우 모든 사용자에게 모든 권한이 열려있습니다. 반면 init.js 파일의 경우 소유자는 읽기쓰기, 그룹과 기타 사용자는 모두 읽기만 가능합니다.

여기까지가 리눅스 파일 권한을 문자열로 표시하는 방법에 대한 설명이었습니다. 리눅스 파일 권한은 숫자로도 나타낼 수 있습니다. 이를 Absolute Mode 혹은 Numeric Mode 라고 합니다. 이 방법을 사용하면 각 사용자의 권한을 0에서 7까지의 숫자 하나로 나타낼 수 있습니다. 문자열에 해당하는 숫자 리스트는 다음과 같습니다.

숫자권한문자열
0권한 없음---
1실행 권한--x
2쓰기 권한-w-
3실행 + 쓰기 권한-wx
4읽기 권한r--
5읽기 + 실행 권한r-x
6읽기 + 쓰기 권한rw-
7모든 권한rwx

이 숫자 표기 방법으로 아까 두 파일의 권한을 다시 나타내면 image.png777 , init.js644 가 됩니다. 이제 이 방법을 사용해서 파일을 찾아보겠습니다. 모든 사용자에게 모든 권한이 열려 있는 파일은 현재 디렉토리에서 다음과 같이 찾을 수 있습니다.

$ find . -perm 777

혹은 소유자 접근 권한은 전부 열려 있고, 나머지 사용자는 읽기만 가능한 파일은 다음과 같이 찾을 수 있습니다.

$ find . -perm 744

소유자로 파일 찾기

-user 혹은 -group 옵션을 사용하여 소유자명이나 그룹명으로 파일을 찾을 수 있습니다. 다시 $ ls -l 명령어를 사용하여 예를 들어 보겠습니다.

저 두 파일 모두 소유자는 hyunochoi, 그룹명은 staff 인 것을 알 수 있습니다. 현재 디렉토리에서 소유자가 hyunochoi 인 파일을 찾는 명령어는 다음과 같습니다.

$ find . -user hyunochoi

혹은 staff 그룹명으로 파일을 찾아도 똑같은 결과가 나올 것입니다.

$ find . -group staff

이렇게 소유자명으로 파일을 찾는 과정은 소유권을 다른 사용자로 바꾸는 과정에서 흔히 쓰입니다. 예를 들어 소유자명이 hyunochoi 인 파일들을 모두 hyunokim 의 소유로 바꾸는 명령을 내릴 수 있습니다.

$ find . -user hyunochoi -type f -exec chown hyunokim {} \;

chown 은 파일 소유자를 바꿀 때 쓰는 명령어입니다. 말 그대로 change owner라는 뜻입니다. 파일을 찾은 뒤 -exec 로 다음 명령을 내리는 방법에 대해서는 밑에서 자세히 다루겠습니다.

파일 찾고 삭제하기

지금까지 사용한 find 명령어의 옵션과 함께 -delete 옵션을 사용하면 찾은 파일을 찾는 것과 동시에 찾은 파일을 모두 삭제할 수 있습니다. 현재 디렉토리에서 파일 확장자가 .png 인 이미지를 모두 제거하는 명령은 다음과 같습니다.

$ find . -name '*.png' -type f -delete

여기서 반드시 반드시 주의해야 할 사항이 있습니다. -delete 옵션은 모든 표현식과 옵션을 쓴 다음 마지막에 붙여야 합니다. 그렇게 하지 않고 -delete 를 맨 앞에 붙이면 명시한 디렉토리상의 모든 파일이 삭제됩니다.

파일 찾고 다음 명령 수행하기

위에서 예로 든 소유자를 바꾸는 명령어처럼 -exec 옵션을 사용해 find 명령으로 찾은 모든 파일을 대상으로 처리할 작업을 지정해줄 수 있습니다. 예를 들어 위의 -delete 옵션을 사용하지 않고 찾은 파일을 모두 제거해보겠습니다.

$ find . -type f -name '*.js' -exec rm {} \;

현재 디렉토리에 있는 js 파일을 모두 제거했습니다. 보이는 바와 같이 {}find 로 찾은 파일들이 들어가는 자리입니다. 또한 명령 마지막에는 \; 를 써서 -exec 명령문이 끝났다는 것을 명시해주어야 합니다.

다른 예를 하나만 더 들어보겠습니다. .png 확장자를 가진 파일의 리스트를 텍스트 파일로 작성하는 명령어입니다.

$ find . -type f -name '*.png' -exec echo {} > list.txt \;

지난 포스팅 내용과 이번 포스팅 내용의 총 복습이라고 할 수 있습니다. 현재 디렉토리에서 png 확장자를 가진 파일을 모두 찾은 후 echo 명령어로 찾은 파일을 모두 출력합니다. 대신, 출력 스트림을 터미널이 아니라 list.txt 파일로 변경합니다. 이제 $ cat list.txt 명령으로 해당 파일을 열어보면 파일 목록이 작성되어 있는 것을 볼 수 있습니다.

fd로 파일 찾기

fdfind 와 유사한 기능을 수행하는 프로그램입니다. 대신 fd 는 색상 출력과 find 보다 간편한 명령어를 지원합니다. fd 는 리눅스 기본 프로그램이 아니므로 설치가 필요합니다. 저는 맥OS 환경이므로 $ brew install fd 명령어로 설치했습니다. 물론 Homebrew가 설치되어 있어야 합니다. 개발 환경별 설치 방법은 fd 깃헙 페이지에 자세히 나와있습니다. https://github.com/sharkdp/fd#installation

이름으로 파일 찾기

대부분의 fd 명령어는 find 명령어를 간편하게 만든 것이기 때문에 자세한 설명은 생략하겠습니다. fd 에서는 간단하게 이름으로 파일을 찾을 수 있습니다.

$ fd init.js

따로 인자를 주지 않는 한, fd 는 전체 파일 시스템을 대상으로 탐색을 수행합니다. 탐색 영역을 특정 디렉토리로 제한하고 싶다면 두 번째 인자로 디렉토리를 넘겨주면 됩니다.

$ fd init.js ./Desktop/test

또한, 아무런 인자 없이 fd 만 입력하면 현재 디렉토리의 모든 파일을 출력합니다.

정규 표현식으로 파일 찾기

파일시스템 상의 모든 자바스크립트 파일을 찾는 명령어를 정규표현식으로 작성할 수 있습니다.

$ fd '.js$'

확장자로 파일 찾기

fd 에서는 -e 옵션 혹은 --extension 으로 특정 확장자를 가진 파일을 검색할 수 있습니다.

$ fd -e js

이 옵션은 파일 이름 찾기와 같이 사용할 수 있습니다. 파일 이름에 init 이 들어가는 js 파일을 찾는 명령어를 다음과 같이 쓸 수 있습니다.

$ fd -e js init

파일 찾은 다음 명령 실행하기

-x 옵션 뒤에 명령어를 적어주면 fd 가 찾은 파일들을 하나씩 인자로 넘겨줍니다. 예를 들어 init.js 파일을 찾고 echo 로 터미널에 출력하는 명령어를 다음과 같이 쓸 수 있습니다.

$ fd init.js -x echo

이렇게 적어주면 echo 명령어에 자동으로 인자가 넘어가게 됩니다. 혹은 {} 를 사용해 찾은 파일들이 들어갈 자리를 직접 명시해줄 수도 있습니다.

$ fd init.js -x echo {}

fd 프로그램 설명을 끝내기 전에 유용한 플레이스 홀더 하나만 정리해보겠습니다. {} 를 사용하면 그 자리에 찾은 파일의 이름이 들어갑니다. 그런데 {.} 를 사용하면 파일의 확장자를 제외한 이름이 들어가게 됩니다. 이 기능은 많은 파일들을 동시에 변환할 때 매우 유용합니다. 제일 처음 예시로 들었던 현재 디렉토리의 모든 PNG 파일을 JPG 로 변환하는 명령어를 작성할 수 있습니다.

$ fd -e PNG . -x convert {} {.}.JPG

locate로 파일 찾기

리눅스 시스템에서는 locate 명령어를 통해 파일을 찾을수도 있습니다. locatefind 와는 다르게 실시간으로 파일 시스템을 뒤지지 않습니다. 대신 파일 시스템을 데이터베이스로 만들어두고 데이터베이스를 기반으로 결과를 출력해줍니다. 따라서 findfd 보다 훨씬 속도가 빠르다는 장점이 있지만, DB가 업데이트 되기 전까지는 새로 생성된 파일을 찾을 수 없다는 단점이 있습니다.

$ locate 명령어를 처음 입력하면 다음과 같이 데이터베이스를 먼저 생성하라는 메시지가 출력됩니다.

안내하는 대로 데이터베이스를 생성해줍니다.

sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.locate.plist

명령어는 엔터를 치면 끝납니다만, 데이터베이스 생성은 백드라운드에서 이루어집니다. 시간이 조금 걸리는 작업이므로 기다려줍시다. 컴퓨터의 파일 개수에 따라 DB 생성에 걸리는 시간에 차이가 있을 수 있습니다.

DB가 생성되었으면 locate 명령어를 사용해보겠습니다. locate 로 모든 js 파일을 찾는 명령어는 다음과 같습니다. locate [option] [pattern] 형태로 사용합니다.

$ locate '*js'

js 파일의 양이 컴퓨터에 상당히 많은데도 불구하고 거의 순식간에 결과가 출력되는 것을 볼 수 있습니다.

locate 명령어의 DB를 수동으로 업데이트 해주려면 다음 명령을 입력합니다.

$ locate updatedb

코드 찾기

지금까지 특정 파일을 찾는 방법에 대해 알아보았습니다. 이제는 파일 내부에서 특정 문자열이 포함된 줄을 찾는 방법에 대해 적어보겠습니다.

grep으로 코드 찾기

리눅스에서는 grep 명령어를 사용합니다. 앞에서 명령 파이프라인에 대해 다룰 때 사용한 적이 있는데, 이번 주제에서 조금 더 구체적으로 정리하겠습니다. grep 역시 강의 노트의 설명이 충분하지 않아 다음 포스팅 내용을 참고해 정리했습니다. https://phoenixnap.com/kb/grep-command-linux-unix-examples

기본 사용법

grep 의 가장 간단한 사용법은 $ grep <문자열> <파일 이름> 입니다. 특수문자나 공백이 포함된 문자열을 찾을 때는 문자열을 '' 로 감싸줍니다. 예를 들어 find.txt 파일 안에서 hello 라는 문자열이 포함된 줄을 찾는 예시는 다음과 같습니다.

보이는 바와 같이 hello 와 정확히 일치하는 문자열만 찾는 것은 아닙니다. 문자열 안에 hello 가 포함되어 있기만 하면 전부 찾아줍니다.

여러 파일에서 문자열 찾기

$ grep <문자열> <파일1> <파일2> <파일3> 와 같이 여러 파일을 한 번에 검색할 수도 있습니다. hello 문자열을 find.txt, find2.txt1, find3.txt 파일에서 찾는 예시는 다음과 같습니다.

여러 파일을 인자로 주면 찾은 문자열 앞에 어떤 파일에서 찾았는지를 표시해줍니다. 이번 예시처럼 파일명이 반복된다면 중괄호를 사용해 다음과 같이 명령어를 작성할 수도 있습니다.

현재 작업 디렉토리 안의 모든 파일에 대해 검색을 수행하고 싶다면 * 와일드카드를 써서 $ grep hello * 와 같이 쓸 수 있습니다.

정확히 매치되는 문자열 찾기

grep 에 아무 옵션도 주지 않으면 찾으려는 문자열과 매치되는 문자열이 포함된 모든 줄을 찾습니다. 즉, $ grep hello * 이라고 입력하면 밑의 모든 문자열들이 검색 대상에 포함됩니다.

  • hello1
  • sayhello
  • helloworld
  • 12hello34

-w 옵션을 사용하면 찾으려는 문자열과 정확히 매치되는 문자열이 포함된 줄만 반환합니다. 위의 예시에서 -w 옵션을 주고 명령을 실행해보겠습니다.

아까와는 다르게 hello 라는 결과 하나만 출력되었습니다.

대소문자 구문 무시하기

모든 탐색 명령어가 그렇듯이, grep 또한 대소문자 무시 옵션을 가지고 있습니다. -i 옵션을 주면 대소문자를 구분하지 않고 문자열이 포함된 줄을 찾습니다.

일부러 find3.txt 파일에 대문자를 넣어보았습니다. 대문자 소문자 모두 출력되는 것을 볼 수 있습니다.

하위 디렉토리 포함

-r 옵션을 사용하면 현재 디렉토리 안에 있는 하위 디렉토리 안의 파일들까지 모두 탐색 대상에 포함됩니다. test 디렉토리 안에 sub 디렉토리를 만들고 그 안에 find4.txt 파일을 생성한 뒤에 $ grep -r hello * 을 실행해보겠습니다.

하위 디렉토리에 있는 파일까지 탐색되었습니다.

역으로 탐색하기

지금까지 인자로 준 문자열과 매치되는 문자열을 탐색했다면 -v 옵션을 통해 인자로 준 문자열을 포함하고 있지 않은 모든 줄을 찾을 수 있습니다.

-v 옵션과 함께 hello 와 매치되지 않는 문자열을 찾았습니다. -i 옵션을 주지 않았으므로 대문자가 포함된 hello 를 포함한 줄도 모두 출력됩니다.

해당 문자열만 포함하고 있는 줄 찾기

지금까지는 해당하는 문자열이 포함된 줄을 찾았습니다. 예를 들어, grep hello find.txt 라는 명령을 실행하면 hello I'm find thank you 라는 줄도 출력 대상에 포함됩니다. -x 옵션을 사용하면 정확히 해당 문자열만 포함하고 있는 줄만 출력합니다.

문자열 포함하고 있는 파일 리스트 출력

때로는 특정 문자열을 포함하고 있는 파일의 리스트가 필요할 수도 있습니다. -l 옵션을 주면 문자열을 포함하는 파일 리스트를 출력해줍니다.

문자열 포함 횟수 출력

-c 옵션을 사용하면 특정 파일이 특정 문자열을 몇 번 포함하고 있는지를 출력할 수 있습니다.

쉘 커맨드 찾기

이제 파일 찾기, 코드 찾기에 이어 입력했던 커맨드를 찾는 방법에 대해 적어보겠습니다. 커맨드를 입력하다 보면 반복되는 커맨드나 긴 커맨드를 계속 치는 것이 귀찮을 때가 있습니다. 이럴 때 이미 쳤던 커맨드를 찾는 방법을 알고 있으면 편하게 사용할 수 있습니다.

위쪽 화살표

커맨드를 찾는 가장 단순한 방법은 위쪽 화살표를 순서대로 누르는 것입니다. 위쪽 화살표를 누를 때마다 이전에 쳤던 커맨드들을 순서대로 순회합니다.

history

$ history 명령어를 입력하면 지금까지 입력했던 명령어 기록을 출력해줍니다.

grep 명령어를 응용해서 history 출력 결과에서 원하는 명령어만 뽑아낼 수도 있습니다. 예를 들어, clear 명령어를 썼던 기록만 결과로 출력하는 명령어를 $ history | grep clear 라고 작성할 수 있습니다.

$ history 를 실행하고 나서 Ctrl + R 을 누르면 히스토리 내 검색 기능이 활성화됩니다. 예를 들어 검색어로 git 을 입력하면 git 이라는 문자가 포함된 가장 최근에 사용한 명령어가 뜹니다. 그 상태에서 Ctrl + R 을 연속해서 누르면 git 이 포함된 명령어들을 순회할 수 있습니다.

autosuggestions

저는 쉘 커맨드를 찾는 방법 중에 autosuggestions이 가장 마음에 들었습니다. 명령어 사용 기록을 기반으로 명령어 자동 완성을 지원해주는 기능입니다. 이 기능은 원래 fish 쉘에 포함된 기능인데, zsh 쉘에서도 플러그인으로 설치해 사용할 수 있습니다. 저는 macOS에서 개발을 진행하고 있으므로 Homebrew를 통해 설치를 진행하겠습니다. 각 개발환경별 설치 방법은 zsh-autosuggestions 깃헙 페이지를 참고해주세요. https://github.com/zsh-users/zsh-autosuggestions

Homebrew를 사용한 설치 방법은 다음과 같습니다.

  1. $ brew install zsh-autosuggestions 실행
  2. .zshrc 파일을 열어 하단에 다음 내용 추가: source $(brew --prefix)/share/zsh-autosuggestions/zsh-autosuggestions.zsh
  3. $ source ~/.zshrc 로 변경된 설정 리로드

이제 아무 명령어나 쳐보면 회색으로 자동 완성 명령어가 뜨는 것을 볼 수 있습니다. 오른쪽 화살표를 눌러 자동 완성을 적용할 수 있습니다.

저 같은 경우 iTerm의 색상 프로파일 설정 때문에 회색 글자가 보이지 않아 애를 먹었는데요, 색상 대비를 높여주니 정상적으로 보이게 되었습니다.

디렉토리 탐색

드디어 Shell Tools의 마지막 파트입니다. 가장 기본적인 디렉토리 탐색 방법은 cd 명령어를 이용하는 것입니다. 그런데 cd 명령어는 사용하기 번거로울 때가 있습니다. 예를 들어 멀리 떨어진 두 디렉토리 사이를 왔다갔다하며 파일을 복사해야 하는 상황에서는 경로 전체를 매번 적어주기 귀찮습니다.

물론 위에서 다룬 명령어 찾기를 활용할 수도 있지만 autojump 라는 간편한 프로그램을 사용할 수 있습니다. autojump이미 방문한 적이 있는 디렉토리로 간편하게 이동할 수 있게 해주는 프로그램입니다. autojump 의 자세한 정보 및 개발환경별 설치 과정은 깃헙 페이지를 참고해주세요. https://github.com/wting/autojump

저는 이번에도 Homebrew 를 사용해서 설치를 진행하겠습니다.

  1. $ brew install autojump 실행
  2. .zshrc 파일을 열고 다음 내용을 .zshrc 하단에 추가: [[ -s brew --prefix/etc/autojump.sh ]] && . brew --prefix/etc/autojump.sh
  3. 터미널 재시작

사용방법은 굉장히 간단합니다. tldr에서 제공하는 사용법을 정리했습니다. 다시 한 번 강조하지만 이동하려는 모든 디렉토리는 한 번 이상 방문한 적이 있어야 합니다!

  • $ j [pattern] : 해당 디렉토리로 점프
  • $ jc [pattern] : 현재 디렉토리의 자식 디렉토리로 점프(같은 이름의 디렉토리가 여러 곳에 있을 때 자식 디렉토리 중 패턴과 일치하는 곳으로 점프)
  • $ jo [pattern] : 해당 디렉토리로 점프하는 대신 해당 디렉토리에서 파일 탐색기 열기
  • $ j --purge : autojump 데이터베이스에서 이제는 존재하지 않는 디렉토리 삭제
  • $ j -s : 데이터베이스 전체 출력

autojump는 특히 멀리 떨어져 있는 두 디렉토리 사이를 오갈 때 진가를 발휘합니다. 인자로 주는 패턴 또한 디렉토리명과 완벽하게 일치할 필요가 없어 cd 명령어와 적절히 섞어 사용하면 좋을 것 같습니다.

이번 포스팅에서는 중괄호 및 와일드카드 사용법과 쉘에서 유용하게 사용할 수 있는 도구들에 대해 알아보았습니다. 다음 포스팅에서는 이번 주차 강의의 연습문제를 풀이하는 시간을 갖도록 하겠습니다.


<참고 문서>

profile
프론트엔드 웹 개발자를 목표로 하고 있습니다.

0개의 댓글