Bash는 세상에 존재하는 유일한 셸은 아니지만, 분명히 가장 강력한 셸 중 하나입니다. 그래서 단순히 명령어 목록을 나열하는 수준을 넘어, 실질적인 애플리케이션을 개발해야 하는 시스템 관리자들에게 Bash가 인기 있는 선택지가 됩니다. 물론 다른 셸도 훌륭한 용도가 많습니다(예를 들어 저는 Git 훅에는 Tcsh를 기본으로 사용합니다). 하지만 진지한 스크립팅 작업이라면 Bash가 손색없는 선택인데, 그 이유를 지금부터 살펴보겠습니다.
함수(Function)
Bash 스크립트에서 함수를 만들려면 function 키워드를 사용합니다:
function foo {
# 여기에 코드 작성
}
함수는 코드의 중복을 줄여주기 때문에 프로그래머에게 매우 유용합니다. 코드 중복이 줄면 유지보수 부담도 줄어듭니다. 값을 변경해야 할 때, 사용되는 모든 위치를 일일이 찾아 수정할 필요 없이 함수 내에서 한 번만 수정하면 되기 때문입니다.
Bash vs. 다른 셸 비교
함수는 대부분의 셸에서 잘 지원되지만, Tcsh는 예외적으로 지원하지 않습니다.
- Korn 셸: Bash와 동일한 문법 사용
- Zsh: Bash와 동일한 문법 사용
- Fish: 함수를 생성하고 조작하기 위해 별도의 function 명령 사용
- Tcsh: 함수가 없다는 점을 goto 문으로 우회할 수 있긴 하지만, 거의 최선의 선택이 아닙니다.
리디렉션(Redirection)
입력과 출력의 리디렉션은 모든 프로그래밍·스크립팅 언어에서 표준적인 기능이며, 셸을 사용하면 자연스럽게 발생합니다. 텍스트를 입력하면 출력을 받게 됩니다. 어떤 셸은 다른 셸보다 더 견고한데, 짐작하시겠지만 Bash는 상상 가능한 거의 모든 옵션을 제공합니다.
기본 원리는 간단합니다. 출력을 리디렉션할 때는 > 문자를, 입력을 리디렉션할 때는 < 문자를 하나 이상 사용합니다. 다만 특정 유형의 입력과 출력에는 특별한 표기가 있습니다. 예를 들어 오류 메시지는 stderr라는 스트림으로 전달되며, 리디렉션 시에는 2>로 표기합니다. 다음 명령은 오류 메시지를 output.log라는 파일로 보냅니다:
$ ls /void 2> output.log
Bash vs. 다른 셸 비교
리디렉션은 셸과 상호작용할 때 큰 편의를 주는 기능입니다. 모든 주요 셸이 일정 수준의 리디렉션을 지원하지만, 모든 셸에서 동일한 기능을 제공하지는 않습니다.
- Korn 셸: Bash와 동일한 문법 사용
- Zsh: Bash와 동일한 문법 사용
- Fish: 부분 지원
- Tcsh: 부분 지원
소스(Source)
셸에서 파일을 소스(source)한다는 것은 해당 파일을 현재 셸 환경으로 가져오는 것을 의미합니다. 대부분의 셸이 이 작업을 능숙하게 처리하지만, 일부 셸은 사용자 편의를 위해 몇 가지 추가 기능을 제공합니다.
예를 들어 Bash에서 source를 사용하면 먼저 현재 디렉터리에서 참조된 파일을 검색합니다. 소스할 파일을 찾지 못하면 이후 PATH에서 검색합니다. 사소한 기능처럼 보이지만 매우 편리합니다. 공통 함수를 드라이브의 한 곳에 중앙 집중식으로 저장해 두고, 자신의 환경을 마치 통합 개발 환경(IDE)처럼 활용할 수 있기 때문입니다. 함수가 어디에 저장되어 있는지 걱정할 필요가 없습니다. 로컬의 /usr/include에 해당하는 위치에 있다는 것을 알고 있으므로, 어디서든 소스하면 Bash가 알아서 찾아줍니다.
Bash vs. 다른 셸 비교
source 명령 또는 축약형(그리고 POSIX 규격에 맞는) . 표기를 사용했을 때, 현재 디렉터리와 PATH 양쪽을 모두 검색하는 셸은 Bash뿐입니다.
키 바인딩(Key bindings)
많은 셸이 사용자와의 상호작용 방식을 커스터마이징할 수 있도록 허용하며, 당연히 텍스트 기반 인터페이스와의 모든 상호작용은 타이핑에서 시작됩니다. Bash에서는 현재 키맵(keymap)을 다음과 같이 확인할 수 있습니다:
$ bind -V | grep keymap
키맵을 변경하려면(예: Emacs에서 Vi로):
$ bind 'set keymap vi'
Bash vs. 다른 셸 비교
키 바인딩에 대한 내장 프리셋을 제공하는 셸은 Bash와 Zsh뿐입니다.
히스토리(History)
Bash는 모든 셸 중에서 가장 강력한 명령 히스토리 인터페이스를 갖추고 있습니다. Bash의 history는 역방향 검색, 빠른 재호출, 히스토리 편집(행 번호로 항목 삭제 포함) 등을 지원합니다. 다른 모든 셸을 합치면 겨우 Bash의 히스토리 인터페이스에 필적할 정도이며, 단독으로는(Bash를 흉내 내는 Zsh조차도) 따라올 수 없습니다.
히스토리 조작
history 명령만이 과거와 상호작용하는 유일한 방법은 아닙니다. Bash는 셸 히스토리에 있는 명령을 불러오고 심지어 수정까지 할 수 있는 풍부한 단축 표기법 모음을 제공합니다.
예를 들어 가장 최근 명령을 다시 실행하려면( history | tail -n1의 출력 결과이며, 히스토리 명령 자체가 히스토리에 남지 않도록 앞에 공백을 넣었다고 가정), Bash에 !!만 입력하면 됩니다:
$ wc -w luarocks.xml 1284 luarocks.xml $ !! 1284 luarocks.xml
가장 최근 명령을 실행하면서 일부 문자열을 다른 내용으로 교체하려면 문자열 치환을 사용할 수 있습니다:
$ wc -w luarocks.xml 1284 luarocks.xml $ ^-w^-l $ wc -l luarocks.xml 214 luarocks.xml
이런 단축키는 매우 다양하며, 모두 Bash의 man 페이지와 info 페이지에 문서화되어 있습니다. 물론 이런 트릭들은 Bash 사용이 반복적이고 단조로워 익숙하지 않은 조합의 키 입력이 오히려 유용해진 고급 사용자들을 위한 것임은 인정해야 합니다. Zsh가 Bash를 효과적으로 흉내 내지만, 이렇게 효율을 극대화하는 기능을 제공하는 셸은 없습니다.
Bash vs. 다른 셸 비교
Bash의 히스토리 명령은 다른 어떤 셸도 따라올 수 없습니다(Zsh가 근접하지만 행 번호로 삭제하는 기능 등 일부 옵션이 없습니다).
연관 배열(Associative arrays)
대부분의 셸은 인덱스 배열(indexed array)을 생성, 조작, 조회하는 기능을 제공합니다. 쉽게 말해 인덱스 배열이란 숫자가 붙은 목록입니다. 이 목록과 할당된 숫자는 하나의 변수에 담겨 코드 안에서 손쉽게 "운반"됩니다.
반면 Bash는 연관 배열(associative array)을 생성하는 기능까지 갖추고 있으며, 이 배열을 다른 배열과 동일하게 취급합니다. 연관 배열을 사용하면 단순히 숫자로 된 값이 아니라 키(key)와 값(value) 쌍의 목록을 만들 수 있습니다.
연관 배열의 좋은 점은 키가 임의적일 수 있다는 것입니다:
$ declare -A userdata $ userdata[name]=seth $ userdata[pass]=8eab07eb620533b083f241ec4e6b9724 $ userdata[login]=`date --utc +%s`
키를 통해 조회합니다:
$ echo "${userdata[name]}"
seth
$ echo "${userdata[login]}"
1583362192
배열에서 기대할 수 있는 대부분의 일반적인 배열 연산을 사용할 수 있습니다.
Bash vs. 다른 셸 비교
연관 배열을 완전히 지원하는 셸은 Bash가 유일합니다(역시 Zsh가 근접하지만 키 목록을 나열하는 함수가 없습니다).
셸 스크립팅에는 Bash를 선택하세요
Bash의 편의 기능 중 일부는 POSIX 표준을 따르지 않습니다. 이론상 그렇기 때문에 Bash가 설치되지 않은 시스템에서는 예상대로 실행되지 않는 Bash 스크립트를 작성할 수도 있습니다. 이런 경우 스크립트가 "이식성(portable)이 없다"고 말합니다.
하지만 실제로는 Bash가 자유 오픈소스 소프트웨어이기 때문에 누구나 설치할 수 있습니다. Linux, BSD, OpenIndiana, Windows, macOS를 사용하든 마찬가지입니다. Bash를 설치한다고 해서 사용자가 Bash를 기본 셸로 사용하거나, 심지어 직접 실행해야 하는 것도 아닙니다. 잘 작성된 셸 스크립트는 첫머리의 shebang 라인(예: #!/bin/bash)에 어떤 셸을 사용하는지 명시하기 때문입니다.
확신이 서지 않는다면 문서에 Bash를 셸 스크립트의 의존성으로 명시하세요(유일한 의존성일지라도). 그러면 사용자에게 이것이 범용 셸 스크립트가 아니라 Bash 전용 스크립트임을 알릴 수 있습니다.
Bash에는 수많은 편의 기능이 있으며, 저는 이러한 장점이 "사용자가 Bash를 설치했는가"라는 걱정보다 더 크다고 생각합니다. Python이나 Java 또는 다른 소프트웨어와 마찬가지로 때로는 의존성이 존재하는 법입니다. Bash를 즐기고 그 단축 표기법과 바로 가기가 유용하다고 느낀다면, 스스로의 편의를 포기하지 마세요.