[리눅스] which 명령어: 실행 파일 위치 출력
리눅스 셸에서 어떤 명령어들은 경로명을 지정하지 않더라도 명령어 이름만으로 실행이 됩니다. 예를 들어 unzip
을 실행하면 다음과 같이 버전 정보와 도움말이 출력됩니다.
$ unzip
UnZip 6.00 of 20 April 2009, by Info-ZIP. Maintained by C. Spieler.
...
그런데 이 명령어는 어느 디렉터리에 있는 걸까요? 이 때 사용할 수 있는 명령어가 바로 which
입니다. 이 글에서는 which 명령어 사용법과 기본적인 동작 원리, 그리고 옵션들을 알아봅니다.
which 명령어로 실행 파일 경로 찾기
which의 첫 번째 인자로 명령어 이름을 넘겨주면 이 실행 파일의 위치를 확인할 수 있습니다. 앞에서 실행해본 unzip
의 경로가 궁금하다면 다음과 같이 실행합니다.
$ which unzip
/usr/bin/unzip
간단하죠? which 명령어는 이 정도만 알고 있어도 활용도가 매우 높으니 꼭 기억해두시기 바랍니다.
자주 사용하는 다른 명령어들의 위치도 한 번 찾아봅니다.
$ which whoami
/usr/bin/whoami
$ which mkdir
/bin/mkdir
$ which ping
/sbin/ping
다들 시스템 어딘가에 있었네요. 명령어의 위치는 배포판이나 셸에 따라서 다를 수 있습니다.
윈도우에는 which
명령어가 없는데, 대신 같은 역할을 하는 where.exe
명령어가 있습니다. 이 명령어에 대해서는 다음 글을 참고해주세요.
$PATH 환경변수와 which 명령어
which를 사용해서 명령어들의 위치를 찾을 수 있습니다만, 여전히 의문은 남을 수 있습니다. which
명령어는 어디에서 이 명령어를 탐색하는 걸까요?
리눅스를 사용하다보면 $PATH
변수에 자주 접할 수 있습니다. 셸에서 어떤 명령어를 전체 경로 없이 실행할 때 기본적으로 이 $PATH
환경변수의 값을 참조합니다. 이 변수에는 디렉터리 목록이 담겨있습니다.
다음 명령어로 현재 $PATH
환경변수 내용을 출력할 수 있습니다.
$ echo $PATH
/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin:/opt/homebrew/sbin
$PATH
에는 다수의 디렉터리가 :
문자로 연결되어있습니다. 다음 명령어로 한 줄 씩 나눠서 경로를 확인할 수 있습니다.
$ IFS=':' PATHS=($(echo $PATH))
$ for string in "${PATHS[@]}"; do echo "$string"; done
/opt/homebrew/bin
/opt/homebrew/sbin
/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin
/opt/homebrew/bin
/opt/homebrew/sbin
앞에서 확인했던 명령어들의 위치는 모두 이 디렉터리 목록에 포함되어있습니다.
그렇다면 정말 $PATH
변수가 which
명령어 동작에 영향을 끼칠까요? $PATH
값을 빈 값으로 변경하고 which
명령어로 unzip
의 위치를 찾아보겠습니다. export
명령어로 PATH 환경변수 값을 지정합니다.
$ export PATH=
$ echo $PATH
이제 $PATH
값은 비어있습니다. which unzip
을 실행해봅니다.
$ which unzip
bash: which: No such file or directory
앗차, $PATH 값이 비어있어서 which
명령어도 찾을 수 없게 되어버렸습니다 😅 which
명령어는 /usr/bin/which
에 있으니 전체 경로를 입력해 명령어를 실행해보겠습니다.
$ /usr/bin/which unzip
아무런 값도 출력되지 않습니다. unzip
을 실행해봐도 해당 실행 파일을 찾을 수 없다는 에러가 출력됩니다.
$ unzip
bash: unzip: No such file or directory
다시 $PATH
변수에 unzip
이 있었던 /usr/bin/
을 추가하고 실행해보겠습니다.
$ export PATH=/usr/bin
$ which unzip
/usr/bin/unzip
$ unzip
UnZip 6.00 of 20 April 2009, by Info-ZIP. Maintained by C. Spieler. Send
bug reports using http://www.info-zip.org/zip-bug.html; see README for details.
....
다시 원래대로 동작하는 것을 확인할 수 있습니다!
$PATH
환경변수와 which
의 동작 원리
그렇다면 여러 디렉터리에 같은 이름을 가진 실행파일이 있는 경우 어떻게 동작할까요? which
명령어는 기본적으로 $PATH
환경변수의 디렉터리 순서를 차례대로 탐색합니다.
간단한 테스트를 위해 이름이 같은 가짜 실행파일 hello_world
를 몇 개 디렉터리에 복사해보겠습니다.
# 임시 디렉터리 생성
$ mkdir -p /tmp/a /tmp/b /tmp/c
# 가짜 명령어 파일 생성
$ touch hello_world
# hello_world 파일에 실행 권한 추가
$ chmod +x hello_world
# 임시 디렉터리들로 hello_world 명령어 복사
$ cp hello_world /tmp/a/hello_world
$ cp hello_world /tmp/b/hello_world
$ cp hello_world /tmp/c/hello_world
다음으로 아래와 같이 $PATH
환경변수를 변경합니다.
$ export PATH='/usr/bin:/tmp/a:/tmp/b:/tmp/c'
이제 which
로 hello_world
명령어 위치를 찾아봅니다.
$ which hello_world
/tmp/a/hello_world
/tmp/a
가 출력됩니다. 이번에는 $PATH
환경변수의 디렉터리 순서를 변경하고 실행해봅니다.
$ export PATH='/usr/bin:/tmp/c:/tmp/b:/tmp/a'
$ which hello_world
/tmp/c/hello_world
이번에는 /tmp/c
가 출력됩니다. 즉, PATH 변수의 디렉터리를 순서대로 탐색해서 처음 만나는 실행 파일의 위치를 알려주는 것을 확인할 수 있습니다.
-a
옵션: 모든 실행 파일 경로 출력
그럼 같은 이름을 가진 실행파일을 모두 찾으려면 어떻게 해야할까요?
이 때는 -a
옵션을 사용하면 됩니다.
$ which -a hello_world
/tmp/c/hello_world
/tmp/b/hello_world
/tmp/a/hello_world
이제 hello_world
명령어가 있는 모든 디렉터리가 출력됩니다.
종료 코드(Exit Status)
which
는 실행 파일을 탐색 성공 여부에 따라서 다른 종료 코드를 가집니다. 이 종료 코드는 셸 스크립팅에서 유용하게 사용할 수 있습니다. 예를 들어 탐색한 명령어가 존재한다면 정상 종료 코드인 0으로 프로그램을 종료합니다. $?
변수를 출력해 마지막 명령어의 종료 코드를 확인할 수 있습니다.
$ which unzip
/usr/bin/unzip
$ echo $?
0
존재하지 않는 실행파일을 탐색하면 종료 코드 1로 프로그램을 종료합니다.
$ which not_existed
$ echo $?
1
잘못된 옵션을 사용하면 2로 종료됩니다.
명령어의 모든 옵션이나 종료 코드는 운영체제나 셸에 따라서 다를 수 있습니다. 이에 대해서는 현재 사용중인 리눅스의 man 페이지를 참고해주세요. 여기까지 리눅스 명령어 which
의 사용법을 알아보았습니다.