셸 스크립트

[섬네일|[ipfirewall 설정을 위해 FreeBSD 셸 스크립트를 편집하는 모습]]
**셸 스크립트**(shell script)는 명령줄 인터프리터인 유닉스 셸에 의해 실행되도록 설계된 컴퓨터 프로그램이다. 셸 스크립트의 다양한 방언들은 명령 언어로 간주된다. 셸 스크립트에 의해 수행되는 전형적인 작업에는 파일 조작, 프로그램 실행, 텍스트 인쇄 등이 포함된다. 환경을 설정하고, 프로그램을 실행하며, 필요한 정리 작업이나 로깅을 수행하는 스크립트를 **래퍼**(wrapper)라고 부른다.

이 용어는 운영 체제 셸을 실행하는 자동화된 모드를 의미하는 것으로 더 일반적으로 사용되기도 한다. 각 운영 체제는 이러한 기능에 대해 특정 명칭을 사용하는데, 여기에는 배치 파일(MS-DOS-윈도우 95 계열, OS/2), 명령 프로시저(VMS), 그리고 셸 스크립트(윈도우 NT 계열 및 Take Command/4NT—관련 문서는 cmd.exe에 있음—와 같은 제3자 파생형)가 포함되며, 메인프레임 운영 체제는 여러 용어와 연관되어 있다.

모든 유닉스 계열 시스템은 적어도 하나 이상의 POSIX 셸(일반적으로 배시 또는 Z 셸 호환 모드)을 포함한다.

## 기능

### 주석
주석은 셸에 의해 무시된다. 주석은 일반적으로 해시 기호(`#`)로 시작하여 줄 끝까지 이어진다.

### 스크립트 언어 선택 설정

셔뱅 또는 해시뱅은 시스템이 파일을 실행하는 데 사용할 인터프리터를 결정하기 위해 사용하는 특별한 종류의 주석이다. 셔뱅은 "`#!`" 문자로 구성된다. 유닉스 계열 운영 체제에서 "`#!`" 접두사 뒤에 오는 문자들은 스크립트를 해석할 실행 프로그램의 경로로 해석된다.

### 단축키
셸 스크립트는 특별한 환경 설정, 명령 옵션 또는 사후 처리가 자동으로 적용되지만, 새로운 스크립트가 여전히 완전히 정상적인 유닉스 명령으로 작동할 수 있는 방식으로 시스템 명령의 편리한 변형을 제공할 수 있다.

한 가지 예로, 파일을 나열하는 명령인 ls의 버전을 만들어 `l`이라는 짧은 명령 이름을 부여하는 것이 있다. 이는 보통 사용자의 `bin` 디렉토리에 `/home/username/bin/l`로 저장되며, 기본 명령 옵션 세트가 미리 제공된다.


```sh
#!/bin/sh
LC_COLLATE=C ls -FCas "$@"
```


여기서 첫 번째 줄은 셔뱅을 사용하여 나머지 스크립트를 실행할 인터프리터를 나타내며, 두 번째 줄은 파일 형식 표시기, 열, 모든 파일(누락 없음), 블록 단위 크기 옵션을 사용하여 목록을 작성한다. `LC_COLLATE=C`는 기본 대조 순서를 설정하여 대소문자를 섞지 않고, 이름의 구두점을 무시하는 부작용으로 점 파일(dotfile)과 일반 파일 이름을 혼합하지 않도록 한다(점 파일은 보통 `-a`와 같은 옵션이 사용될 때만 표시됨). 또한 `"$@"`는 `l`에 주어진 모든 매개변수가 ls의 매개변수로 전달되도록 하여, ls가 알고 있는 모든 일반 옵션과 기타 문법을 여전히 사용할 수 있게 한다.

그러면 사용자는 가장 자주 사용되는 짧은 목록 작성을 위해 단순히 `l`을 사용할 수 있다.

단축키로 사용될 수 있는 셸 스크립트의 또 다른 예는 주어진 디렉토리 내의 모든 파일과 디렉토리 목록을 인쇄하는 것이다.


```sh
#!/bin/sh

clear
ls -al
```


이 경우, 셸 스크립트는 일반적인 시작 줄인 #!/bin/sh로 시작한다. 그 후 스크립트는 다음 줄로 넘어가기 전에 터미널의 모든 텍스트를 지우는 clear 명령을 실행한다. 다음 줄은 스크립트의 주요 기능을 제공한다. ls -al 명령은 스크립트가 실행되는 디렉토리에 있는 파일과 디렉토리를 나열한다. ls 명령의 속성은 사용자의 필요에 맞게 변경될 수 있다.

### 배치 작업
셸 스크립트는 명령줄 인터페이스에서 수동으로 입력해야 하는 여러 명령을 사용자가 각 단계의 트리거를 기다릴 필요 없이 자동으로 실행할 수 있게 한다. 예를 들어, 세 개의 C 소스 코드 파일이 있는 디렉토리에서 최종 프로그램을 빌드하는 데 필요한 네 개의 명령을 수동으로 실행하는 대신, POSIX 준수 셸을 위한 `build`라는 이름의 스크립트를 만들어 자동으로 컴파일할 수 있다.


```sh
#!/bin/sh
printf 'compiling...\n'
cc -c foo.c
cc -c bar.c
cc -c qux.c
cc -o myprog foo.o bar.o qux.o
printf 'done.\n'
```


이 스크립트를 통해 사용자는 편집 중인 파일을 저장하고 에디터를 일시 중단한 다음, `./build`를 실행하여 업데이트된 프로그램을 생성하고 테스트한 뒤 다시 에디터로 돌아갈 수 있다. 하지만 1980년대 이후 이러한 유형의 스크립트는 프로그램 빌드에 특화된 make와 같은 유틸리티로 대체되었다.

### 일반화
단순한 배치 작업은 고립된 작업에서 드문 일이 아니지만, 셸 루프, 테스트 및 변수를 사용하면 사용자에게 훨씬 더 많은 유연성을 제공한다. 명령줄에서 제공된(와일드카드를 통할 수도 있는) JPEG 이미지를 PNG 이미지로 변환하는 POSIX sh 스크립트는 다음과 같이 작성할 수 있으며, 보통 `/home/username/bin/jpg2png`와 같은 파일에 저장된다.


```sh
#!/bin/sh
for jpg; do                                  # 주어진 각 파일 이름에 대해 차례로 $jpg를 사용
    png=${jpg%.jpg}.png                      # .jpg를 .png로 교체하여 PNG 버전 파일 이름 구성
    printf 'converting "%s" ...\n' "$jpg"    # 스크립트를 실행하는 사용자에게 상태 정보 출력
    if convert "$jpg" jpg.to.png; then       # convert(ImageMagick 제공)를 사용하여 임시 파일에 PNG 생성
        mv jpg.to.png "$png"                 # 성공하면 임시 PNG 이미지 이름을 올바른 이름으로 변경
    else                                     # ...그렇지 않으면 오류를 알리고 스크립트 종료
        printf >&2 'jpg2png: error: failed output saved in "jpg.to.png".\n'
        exit 1
    fi                                       # "if" 테스트 구조의 끝
done                                         # "for" 루프의 끝
printf 'all conversions successful\n'        # 사용자에게 좋은 소식을 알림
```


그러면 `jpg2png` 명령은 `/home/username/bin/jpg2png *.jpg` 만으로 JPEG 이미지로 가득 찬 전체 디렉토리에서 실행될 수 있다.

### 프로그래밍
많은 현대적인 셸은 제어 흐름 구조, 변수, 주석, 배열, 서브루틴 등과 같이 정교한 범용 프로그래밍 언어에서만 볼 수 있는 다양한 기능도 제공한다. 이러한 기능을 통해 셸 스크립트로 상당히 정교한 애플리케이션을 작성하는 것이 가능하다. 그러나 대부분의 셸 언어는 데이터 타이핑 시스템, 클래스, 스레딩, 복잡한 수학 및 기타 일반적인 전체 언어 기능에 대한 지원이 거의 없거나 아예 없으며, 컴파일된 코드나 성능을 목적으로 작성된 인터프리터 언어보다 일반적으로 훨씬 느리다는 점 때문에 여전히 제한적이다.

표준 유닉스 도구인 sed와 awk는 셸 프로그래밍을 위한 추가 기능을 제공하며, 펄이나 Tcl 같은 다른 스크립트 언어도 셸 스크립트에 내장될 수 있다. 펄과 Tcl은 그래픽 툴킷도 함께 제공된다.

## 전형적인 셸 언어

유닉스, 리눅스 및 POSIX 준수 운영 체제 설치 시 흔히 발견되는 스크립트 언어는 다음과 같다.
* 원래의 본 셸 (`sh`), 현재는 흔히 쓰이지 않음
* GNU 배시 (`bash`)
* Debian Almquist shell (`dash`)
* 호환 모드로 실행되는 Z 셸 (`zsh`)
흔히 사용되는 비 POSIX 셸은 다음과 같다.
* Nushell (`nu`)
* Fish shell (`fish`)
* xonsh 셸 (`xonsh`), "콘치"라고 읽으며 파이썬 기반 셸임
* 파워셸 (`pwsh`)

C 및 Tcl 셸은 해당 프로그래밍 언어와 상당히 유사한 문법을 가지고 있으며, 콘셸과 배시는 본 셸의 발전된 형태이다. 본 셸은 알골 언어를 기반으로 하며 여러 다른 언어의 요소가 추가되었다. 반면, 다양한 셸과 awk, sed, Grep 같은 도구, 그리고 베이직, 리스프, C 등은 펄 프로그래밍 언어에 기여했다.

이전에 인기가 있었던 셸은 다음과 같다.

* Old shell (`osh`), "유닉스 제6판의 표준 명령 인터프리터 포트"
* 콘셸 (`ksh`), 이후 대부분 후속작인 Z 셸로 대체됨
* Tenex C Shell (`tcsh`) 및 그 전신인 C 셸 (`csh`)

다양한 형태의 파이썬, 루비, C, 자바, 펄, 파스칼, REXX 등을 기반으로 하는 셸과 같은 관련 프로그램들도 널리 사용 가능하다.

다음과 같은 소위 원격 셸들은

* 리모트 셸 (`rsh`)
* 시큐어 셸 (`ssh`)
실제로는 원격 시스템에서 더 복잡한 셸을 실행하기 위한 도구일 뿐이며, 그 자체로는 '셸'과 같은 특성을 가지고 있지 않다.

## 기타 스크립트 언어

파이썬, 펄, Tcl과 같은 많은 강력한 스크립트 언어들이 전통적인 셸 스크립트로 처리하기에는 너무 크거나 복잡하거나 반복적인 작업을 해결하기 위해 도입되었으며, 동시에 C나 자바와 같은 컴파일 언어와 관련된 오버헤드를 피한다.

스크립트 언어와 범용 고수준 프로그래밍 언어 사이의 구분은 종종 논쟁의 대상이 되지만, 스크립트 언어는 일반적으로 인터프리터 방식의 특성, 단순화된 문법, 그리고 작업 자동화, 시스템 운영 조정 및 구성 요소 간의 "글루 코드" 작성에 주로 사용되는 특징을 가진다. 파이썬이나 자바스크립트와 같은 스크립트 언어가 성능 향상을 위해 바이트코드로의 컴파일을 지원하거나 JIT를 사용하더라도, 역사적으로 자동화, 경량화된 도구 및 독립형 애플리케이션 개발보다는 스크립팅 환경과의 연관성 때문에 여전히 흔히 "스크립트 언어"라고 불린다.

중요한 것은 스크립팅이 프로그래밍의 한 형태라는 점이다. "스크립팅"은 가볍고 작업 지향적인 자동화를 강조할 수 있지만, 더 넓은 의미의 "프로그래밍"은 스크립팅과 컴파일 또는 구조화된 언어에서의 소프트웨어 개발을 모두 아우른다. 따라서 스크립팅은 컴퓨터가 특정 작업을 수행하도록 지시하는 코드를 작성하는 것을 포함하며, 이는 프로그래밍의 근본적인 정의를 충족한다.

## 생애 주기
셸 스크립트는 소프트웨어 개발의 초기 단계로 자주 쓰이며, 나중에 다른 기본 구현으로 전환되는 경우가 많다. 가장 흔하게는 펄, 파이썬, 또는 C로 변환된다. 인터프리터 지시문(셔뱅)을 사용하면 구현 세부 사항을 파일 확장자로 노출하는 대신 스크립트 내부에 완전히 숨길 수 있으며, 최종 사용자에게 영향 없이 다른 언어로 원활하게 재구현할 수 있다.

".sh" 파일 확장자를 가진 파일은 대개 일종의 셸 스크립트이지만, 대부분의 셸 스크립트는 파일 확장자를 가지지 않는다.

## 장점과 단점
셸 스크립트를 작성하는 가장 큰 장점은 명령과 문법이 명령줄에서 직접 입력하는 것과 완전히 동일하다는 점일 것이다. 프로그래머는 스크립트가 다른 언어로 작성되었거나 컴파일된 언어가 사용되었을 때처럼 완전히 다른 문법으로 전환할 필요가 없다.

종종 셸 스크립트를 작성하는 것이 다른 프로그래밍 언어로 동등한 코드를 작성하는 것보다 훨씬 빠르다. 쉬운 프로그램이나 파일 선택, 빠른 시작, 대화형 디버깅 등 많은 장점이 있다. 셸 스크립트는 기존 프로그램들 사이에서 시퀀싱 및 의사 결정 연결을 제공하는 데 사용될 수 있으며, 중간 규모의 스크립트에서는 컴파일 단계가 없다는 점이 장점이다. 인터프리터 방식의 실행은 스크립트에 디버깅 코드를 작성하고 이를 다시 실행하여 버그를 탐지하고 수정하는 것을 쉽게 만든다. 비전문가 사용자도 스크립팅을 사용하여 프로그램의 동작을 맞춤화할 수 있으며, 셸 스크립팅은 멀티프로세싱을 위한 제한된 범위를 제공한다.

반면에 셸 스크립팅은 비용이 많이 드는 오류가 발생하기 쉽다. 의도한 `rm -rf */` 대신 `[[Rm (유닉스)|rm]] -rf * /`와 같은 부주의한 타이핑 오류는 유닉스 커뮤니티의 민담과도 같다. 단 하나의 추가 공백이 현재 디렉토리에 포함된 모든 하위 디렉토리를 삭제하는 명령을 파일 시스템의 루트 디렉토리부터 모든 것을 삭제하는 명령으로 바꿔버린다. 유사한 문제들이 cp와 Mv를 위험한 무기로 변질시킬 수 있으며, `>` 리다이렉트의 오용은 파일 내용을 삭제할 수 있다.

또 다른 중요한 단점은 느린 실행 속도와 실행되는 거의 모든 셸 명령에 대해 새로운 프로세스를 시작해야 한다는 점이다. 스크립트의 작업이 효율적인 필터 명령이 대부분의 작업을 수행하는 파이프라인을 설정함으로써 달성될 수 있을 때는 속도 저하가 완화되지만, 복잡한 스크립트는 일반적으로 동등한 작업을 수행하는 기존의 컴파일된 프로그램보다 몇 배는 느리다.

서로 다른 플랫폼 간의 호환성 문제도 존재한다. 펄의 창시자인 래리 월은 "셸 스크립트보다 셸 자체를 포팅하는 것이 더 쉽다"라는 유명한 말을 남겼다.

마찬가지로 더 복잡한 스크립트는 셸 스크립트 언어 자체의 한계에 부딪힐 수 있다. 이러한 한계는 품질 좋은 코드를 작성하기 어렵게 만들며, 원래 셸 언어의 문제를 개선하기 위한 다양한 셸의 확장 기능들이 오히려 문제를 악화시킬 수도 있다.

일부 스크립트 언어 사용의 많은 단점은 언어 문법이나 구현 내의 설계 결함으로 인해 발생하며, 반드시 텍스트 기반 명령줄을 사용한다고 해서 발생하는 것은 아니다. 다른 셸 프로그래밍 언어를 사용하거나 심지어 스킴을 사용하는 Scsh와 같이 본격적인 언어를 사용하는 셸들도 존재한다.

## 스크립트 언어 간의 상호운용성

많은 스크립트 언어들은 POSIX 표준을 준수하기 때문에 유사한 문법과 기능을 공유하며, 여러 셸은 다른 셸을 에뮬레이트하거나 호환성을 유지하기 위한 모드를 제공한다. 이를 통해 특정 셸을 위해 작성된 스크립트가 최소한의 변경만으로 다른 셸에서 실행될 수 있는 경우가 많다.

예를 들어, 배시는 원래의 본 셸 문법을 많이 지원하며 이식성을 높이기 위해 POSIX 준수 모드를 제공한다. 그러나 배시에는 POSIX에서 찾을 수 없는 다수의 확장 기능이 포함되어 있는데, 이를 흔히 bashisms라고 부른다. 이러한 기능들은 스크립팅 능력을 향상시키지만, Dash나 ksh와 같은 다른 셸과의 호환성을 떨어뜨릴 수 있다.

## 다른 운영 체제에서의 셸 스크립팅

시큐인, MKS 툴킷, 인테릭스(이전의 마이크로소프트 Windows Services for UNIX), Hamilton C shell, UWIN(AT&T Unix for Windows)과 같은 상호운용성 소프트웨어는 유닉스 셸 프로그램이 윈도우 NT 기반 시스템에서 실행될 수 있게 한다. 다만 일부 기능은 오래된 MS-DOS/윈도우 95 플랫폼에서 완전히 지원되지 않을 수 있다. MKS 툴킷의 초기 버전은 OS/2에 대한 지원도 제공했다.

또한 윈도우 환경에서 여러 DCL(Digital Command Language) 구현을 사용할 수 있다. 여기에는 윈도우 명령 셸과 통합되고 CGI 프로그래밍 및 윈도우 스크립트 호스트를 위한 자동화를 지원하는 XLNT와 같은 스크립팅 도구가 포함된다. macOS(이전의 Mac OS X) 또한 유닉스 기반이며 기본적으로 POSIX 준수 셸 환경을 포함한다.

4DOS, 4OS2, 프리도스, 피터 노턴의 NDOS 및 Take Command(이전의 4NT)와 같은 콘솔 대안들은 각각 윈도우 NT 스타일의 cmd.exe, MS-DOS/윈도우 95 배치 파일(Command.com에 의해 실행됨), OS/2의 cmd.exe 및 4NT에 기능을 추가한다. 이들은 강화 대상이 되는 셸과 유사하며 윈도우 스크립트 호스트와 더 긴밀하게 통합되어 있다. 윈도우 스크립트 호스트는 VB스크립트, J스크립트, VBA 등 세 가지 사전 설치된 엔진과 함께 제공되며, 4NT 및 관련 프로그램에서는 REXX, 펄, 파이썬, 루비, Tcl 등이 사전에 정의된 함수를 가지고 있는 수많은 제3자 엔진을 추가할 수 있다. PC DOS는 MS-DOS와 상당히 유사하며, DR DOS는 더 차이가 있다. 이전 버전의 윈도우 NT는 OS/2 서브시스템을 통해 당시의 4OS2 버전을 실행할 수 있었다.

스크립트 언어는 정의상 확장이 가능하다. 예를 들어 MS-DOS/윈도우 95/98 및 윈도우 NT 계열 시스템은 셸/배치 프로그램이 KiXtart, 큐베이직, 다양한 베이직, REXX, 펄, 파이썬 구현체, 윈도우 스크립트 호스트 및 설치된 엔진과 같은 도구를 호출할 수 있게 한다. 유닉스 및 기타 POSIX 준수 시스템에서 awk와 sed는 셸 스크립트의 문자열 및 숫자 처리 능력을 확장하는 데 사용된다. Tcl, 펄, Rexx 및 파이썬은 그래픽 툴킷을 가지고 있으며, 속도 병목 현상이 발생하는 셸 스크립트의 함수 및 프로시저를 코딩하는 데 사용될 수 있고(C, 포트란, 어셈블리어 등이 훨씬 더 빠름), 소켓 및 기타 연결 기능, 고성능 텍스트 처리, 호출 스크립트에 기능이 없는 경우의 숫자 작업, 자기 작성 및 자기 수정 코드, 재귀, 직접 메모리 액세스, 다양한 유형의 정렬 등 메인 스크립트에서는 어렵거나 불가능한 기능을 추가하는 데 사용될 수 있다. 비주얼 베이직 포 애플리케이션 및 VB스크립트는 스프레드시트, 데이터베이스, 모든 유형의 스크립트 가능 프로그램, 통신 소프트웨어, 개발 도구, 그래픽 도구 및 컴포넌트 오브젝트 모델을 통해 액세스할 수 있는 기타 소프트웨어를 제어하고 통신하는 데 사용될 수 있다.

## 같이 보기
* 윈도우 파워셸
* 윈도우 스크립트 호스트
* 셔뱅
* 스크립트 언어

## 각주

## 외부 링크

* ''An Introduction To Shell Programming'' by Greg Goebel
* ''UNIX / Linux shell scripting tutorial'' by Steve Parker
* ''Shell Scripting Primer'' (Apple) 
* ''What to watch out for when writing portable shell scripts'' by Peter Seebach
* Free Unix Shell scripting books

분류:스크립트 언어