이 문서는 Bash를 배우고 싶은 사람을 위해 작성되었지만 깊은 내용을 다루지는 않습니다.
Tip: learnyoubash - 이 핸드북 내용에 대해서 interactive한 워크숍을 진행할 수 있습니다.
npm
을 이용해서 이 핸드북을 설치할 수 있습니다. 다음 명령어를 실행하십시오:
$ npm install -g bash-handbook
설치 후 bash-handbook
명령을 실행합니다. $PAGER
선택자를 이용하여 매뉴얼 페이지를 확인할 수 있습니다. 아니면 계속 여기서 읽으셔도 됩니다.
소스 코드는 여기에 있습니다: https://github.com/denysdovhan/bash-handbook
- Introduction
- Shells and modes
- Comments
- Variables
- Shell expansions
- Arrays
- Streams, pipes and lists
- Conditional statements
- Loops
- Functions
- Debugging
- Afterword
- Other resources
- License
당신이 개발자라면 시간이 얼마나 가치있는 자원인지 알고 있을 것입니다. 업무 프로세스를 최적화하는 것은 일을 진행하는데 있어서 가장 중요한 측면 중 하나입니다.
다음과 같이 몇 번이고 반복해야하는 작업에서는 효율성과 생산성을 높이기 위한 방법이 필요합니다:
- 서버에서 스크린샷을 찍거나 업로딩하는 경우
- 많은 텍스트들을 보기 좋게 만들거나 서식에 맞추는 경우
- 파일을 다른 포맷으로 변경하는 경우
- 프로그램에서 나오는 출력을 구문해석하는 경우
이런 작업에서는 Bash가 바로 우리의 구세주입니다!
Bash는 Bourne shell을 대체할 GNU 프로젝트로 [Brain Fox][]의해 작성된 Unix 셸입니다. 1989년에 배포되었으며, Linux와 macOS에서 기본 셸로 오랫동안 사용되었습니다.
그럼 우리는 왜 30년도 더 된 기술을 배워야 될까요? 대답은 간단합니다: 이 _something_은 오늘날 모든 Unix 기반 시스템에서 효율적인 스크립트를 작성하기 위한 가장 강력한 도구 중 하나이며, 이것이 당신이 Bash를 배워야 하는 이유입니다. 이상.
이 핸드북은 bash에서 사용하는 가장 중요한 개념을 설명하고 있습니다. 난 이 요약한 것이 당신에게 도움이 되었으면 합니다.
Bash 셸은 interactive 모드와 non-interactive 모드, 두가지 모드로 작업이 가능합니다.
당신이 Ubuntu에서 작업을 하고 있다면, 7개나 되는 가상 터미널을 사용할 수 있습니다. 데스크톱 환경에서 7번째 가상 터미널인 GUI 모드로 돌아오고 싶다면 Ctrl-Alt-F7
을 이용하여 GUI를 사용할 수 있습니다.
Ctrl-Alt-F1
키를 이용하여 셸을 열 수 있습니다. 그러면 친숙한 GUI는 사라지고 1번 가상 터미널이 보입니다.
Interactive 모드에서 작업 중이라면 다음과 같이 보입니다:
user@host:~$
여기서 여러 가지 Unix 명령어를 입력할 수 있습니다. ls
, grep
, cd
, mkdir
, rm
과 같은 명령을 실행하고 결과값을 확인할 수 있습니다.
셸이 사용자와 직접 상호 작용을 하기에 우리는 셸과 interactive가 가능합니다.
가상 터미널 사용은 불편합니다. 예로, 한 번에 문서를 수정하고, 다른 명령어를 사용하는 경우라면 다음의 가상 터미널 에뮬레이터를 사용하는 것이 좋습니다:
Non-interactive 모드에서는 셸에서 파일이나 pipe를 통하여 명령을 읽고 실행합니다. Interpreter는 파일이 끝나게 되면 셸 프로세스는 세션을 종료하고 부모 프로세스로 돌아갑니다.
다음 명령으로 non-interactive 모드로 셸을 실행시킬 수 있습니다:
sh /path/to/script.sh
bash /path/to/script.sh
위 예제에서 script.sh
는 셸 인터프리터가 알 수 있는 명령으로 구성된 일반 파일이고 sh
, bash
는 셸 인터프리터 프로그램입니다. 당신은 script.sh
파일을 편한 에디터로 작성할 수 있습니다. (예, vim, nano, Sublime Text, Atom 등)
chmod
명령을 통해 실행파일로 변경해야 스크립트를 쉽게 실행할 수 있습니다.
chmod +x /path/to/script.sh
또한 스크립트 첫 줄은 파일에서 실행하는 데 사용되는 프로그램을 다음과 같이 표시해야 합니다:
#!/bin/bash
echo "Hello, world!"
bash
보다 sh
가 더 좋다면, #!/bin/bash
구문을 #!/bin/sh
로 변경하면 됩니다. 이 #!
문자열은 shebang로 알려져 있습니다. 그럼 이제 다음처럼 스크립트를 실행할 수 있습니다:
/path/to/script.sh
위에서 작성한 간단한 trick은 echo
명령을 이용하여 터미널에 텍스트를 출력합니다.
Shebang 라인에 다음과 같은 방법으로 입력할 수도 있습니다:
#!/usr/bin/env bash
echo "Hello, world!"
이 shebang에서 좋은 점은 시스템 PATH
환경 변수로 지정된 것을 기반으로 프로그램을 (여기서는 bash
) 실행한다는 것입니다. 이것은 파일 시스템에서 프로그램 path가 같지 않을 수 있기에 첫 번째 방법보다는 바람직합니다. 시스템 PATH
가 다른 버전에 대한 프로그램으로 표기하고 있을 때 유용합니다. 예를 들어, 기존 버전인 bash
를 유지하면서 새로운 버전으로 설치하고 PATH
변수에 새로운 위치를 입력하여 사용할 수 있습니다. 기존 bash
를 사용하기 위해서는 #!/bin/bash
를 사용하고 새로운 버전을 사용하기 위해 #!/usr/bin/env bash
를 사용하면 됩니다.
모든 명령은 exit code를 반환합니다 (return status 또는 exit status). 정상 작동한 명령은 항상 0
(zero-code)를, 정상 작동이 안된 경우 0이 아닌 값(error code)을 반환합니다. 실패 코드는 1에서 255까지인 양의 정수이어야 합니다.
스크립트로 사용할 때 우리가 사용할 수 있는 유용한 명령 중 하나는 exit
입니다. 이 명령은 실행되고 있는 명령을 종료하고 셸에 종료 코드를 전달하는 데 사용합니다. 인수 없이 exit
코드를 실행하면 실행 중인 스크립트가 종료된 후 exit
전 마지막으로 실행한 명령이 반환한 종료 코드를 반환하게 됩니다.
프로그램이 종료되면 셸은 $?
환경변수에 그 exit code가 할당됩니다. $?
변수는 스크립트가 실행되었는지를 확인할 수 있는 기본 테스트 방법입니다.
마찬가지로 우리는 스크립트를 종료하기 위해 exit
를 사용할 수 있고, return
명령을 사용하여 함수를 종료하고 caller에게 exit code를 반환합니다. 함수 내부에서 exit
를 사용할 수 있으며, 함수를 종료_하고_ 프로그램을 죽입니다.
스크립트는 _주석_을 포함해도 됩니다. 주석은 shell
인터프리터에서 무시되는 특별한 문장입니다. #
으로 시작하며 줄 바꿈이 있기 전까지 작성된 내용은 주석 처리됩니다.
예를 들어:
#!/bin/bash
# This script will print your username.
whoami
Tip: 왜 스크립트를 만들었는지에 대한 내용을 주석에 넣어야 됩니다.
다른 프로그래밍 언어처럼 변수를 생성하여 사용할 수 있습니다.
Bash는 데이터 타입을 알 수 없습니다. 그래서 변수는 오직 숫자(numbers)나 하나 이상의 문자열(string)로 할당할 수 있습니다. 변수는 3가지로 만들 수 있습니다: 지역 변수, 환경 변수, positional arguments 변수입니다.
**지역 변수 (Local variables)**는 단일 스크립트 내에 존재하는 변수입니다. 다른 프로그램이나 스크립트에서 접근할 수 없습니다.
지역 변수는 =
기호를 이용하여 할당할 수 있습니다. 규칙상 =
과 값 사이에는 공백이 있어서는 안 됩니다. 그리고 값을 부를 때는 $
기호를 이용하여 부릅니다. 예를 들어:
username="denysdovhan" # declare variable
echo $username # display value
unset username # delete variable
또한 local
키워드를 사용하여 단일 함수에 지역 변수를 선언 할 수 있습니다. 이렇게 하면 함수가 종료할 때 변수가 표시되지 않습니다.
local local_var="I'm a local value"
**환경 변수 (Environment variables)**는 현재 셸 섹션에서 실행되고 있는 프로그램이나 스크립트에서 접근할 수 있는 변수입니다. 지역 변수와 같이하여 만들지만, export
키워드를 사용하여 생성합니다.
export GLOBAL_VAR="I'm a global variable"
Bash에서는 전역 변수가 좀 있습니다. 자주 이런 변수를 확인하기 위해 순람표가 있습니다:
변수 | 설명 |
---|---|
$HOME |
현재 사용자 홈 디렉터리. |
$PATH |
셸에서 사용하는 명령어를 찾기 위한 위치. 콜론으로 목록을 구분함. |
$PWD |
현재 작업 중인 디렉터리. |
$RANDOM |
0 ~ 32767 사이의 렌덤 인트값. |
$UID |
현자 사용자의 실재 사용자 ID, 숫자로 표기됨. |
$PS1 |
첫번째 프롬프트 문자열. |
$PS2 |
두번째 프롬프트 문자열. |
다음에 링크에서 bash에 있는 환경 변수 확장 목록을 확인할 수 있습니다.
**위치 매개변수 (Positional parameters)**는 함수는 값을 구하고 할당된 위치에 대한 값을 할당합니다. 함수 내부에 있는 경우, 다음 표에 나열된 위치 매개 변수와 기타 특별한 변수에 대한 의미를 보여줍니다. (역자주: Argument라고 불리는 매개변수들이다.)
매개변수 | 설명 |
---|---|
$0 |
스크립트 이름. |
$1 … $9 |
1 ~ 9까지 매개변수 목록 요소. |
${10} … ${N} |
10 ~ N까지 매개변수 목록 요소. |
$* or $@ |
$0 을 제외한 모든 위치 매개변수. |
$# |
매개변수에 대한 숫자, $0 를 제외하고. |
$FUNCNAME |
함수 이름 (함수 내에서만 값을 가집니다) |
다음 예제를 위치 매개변수로 나타내면 $0='./script.sh'
, $1='foo'
, $2='bar'
:
./script.sh foo bar
변수에 기본 값이 존재할 수 있습니다. 다음 구문으로 해당 값을 정의하면 가능합니다:
# if variables are empty, assign them default values
: ${VAR:='default'}
: ${$1:='first'}
# or
FOO=${FOO:-'default'}
_Expansions(확장 or 전개식)_은 _token_으로 나눠진 후 커멘드 라인에서 실행됩니다. 계산 산술 연산 메커니즘에서 다른 단어들은 명령이 실행되고 값이 저장됩니다.
여기에 흥미가 있다면 more about shell expansions를 보십시오.
중괄호 expansion은 임의의 문자열을 생성할 수 있습니다. _Filename expansion_과 비슷합니다. 예로:
echo beg{i,a,u}n # begin began begun
또한 중괄호 expansion는 루프에서 반복할 범위를 생성할 때 사용할 수 있습니다.
echo {0..5} # 0 1 2 3 4 5
echo {00..8..2} # 00 02 04 06 08
명령 치환은 우리가 명령에 대한 값을 구하고 다른 명령이나 변수를 할당하는데 그 값을 할당할 수 있습니다. 명령은 ``
이나 $()
로 묶였을 때 명령어로 치환이 됩니다. 예를 들어 다음과 같이 사용할 수 있습니다:
now=`date +%T`
# or
now=$(date +%T)
echo $now # 19:08:26
Bash에서의 산술 연산은 자유롭게 사용할 수 있습니다. 그러나 수식인 경우엔 $(())
로 묶어 사용합니다. 산술 expansion 형식은:
result=$(( ((10 + 5*3) - 7) / 2 ))
echo $result # 9
산술 expansion에서 변수는 일반적으로 $
접두사 없이 사용합니다:
x=4
y=7
echo $(( x + y )) # 11
echo $(( ++x + y++ )) # 12
echo $(( x + y )) # 13
큰따옴표와 작은따옴표 사이에는 중요한 차이가 있습니다. 큰따옴표 안에서 변수나 명령 치환이 진행됩니다. 작은따옴표 안에서는 그렇지 않습니다. 예를 들면:
echo "Your home: $HOME" # Your home: /Users/<username>
echo 'Your home: $HOME' # Your home: $HOME
공백이 포함되어있는 경우, 따옴표 안에 지역 변수와 환경 변수 확장시 주의합니다. 지루한 예제로 echo
를 이용하여 사용자가 입력한 값에 대하여 출력하는 것을 고려하십시오:
INPUT="A string with strange whitespace."
echo $INPUT # A string with strange whitespace.
echo "$INPUT" # A string with strange whitespace.
첫 번째 echo
는 $INPUT 변수에 있는 단어들을 5개인 변수로 인식하여 한 칸씩 띄워 echo
로 출력합니다. 두 번째 echo
는 공백을 포함한 $INPUT 변수 전체 값을 단일 인수로 받아들여 출력합니다.
이제 더 복잡한 경우를 봅시다:
FILE="Favorite Things.txt"
cat $FILE # attempts to print 2 files: `Favorite` and `Things.txt`
cat "$FILE" # prints 1 file: `Favorite Things.txt`
이련 경우, Favorite-Things.text
와 같은 이름을 파일 이름을 변경해서 해결할 수 있지만, 환경 변수, 위치 매개변수, 다른 명령 (find
, cat
등)을 이용하여 출력하는 것을 확인해야 됩니다. 만약 입력값에 공백이 포함되어 있을지도 모른다면 따옴표로 감쌉니다.
다른 프로그래밍 언어들처럼 bash에서 배열(array)는 여러 값을 참조할 수 있도록 해주는 변수입니다. Bash에서 배열은 zero-based입니다. 이건 배열 첫 요소 인덱스가 0이라는 것을 의미합니다.
배열을 사용할 땐, 특별한 환경 변수인 IFS
를 조심해야 합니다. IFS, Input Field Separator 라 불리는 이 환경 변수는 배열 요소를 구별하는 문자입니다. 기본 값은 빈 스페이스 값입니다. IFS=' '
.
Bash는 단순하게 배열 변수에 인덱스 값을 할당하여 변수를 생성합니다:
fruits[0]=Apple
fruits[1]=Pear
fruits[2]=Plum
또한 배열 변수는 여러 가지를 함께 할당하여 사용할 수 있습니다:
fruits=(Apple Pear Plum)
각 배열 요소는 다른 변수와 같이 사용할 수 있습니다:
echo ${fruits[1]} # Pear
전체 배열에서 인덱스를 대신해서 *
, @
로 확장하여 사용할 수 있습니다:
echo ${fruits[*]} # Apple Pear Plum
echo ${fruits[@]} # Apple Pear Plum
이 두 예제에는 중요하고 미묘한 차이가 있습니다: 공백을 가지고 있는 배열 요소가 있는 경우:
fruits[0]=Apple
fruits[1]="Desert fig"
fruits[2]=Plum
배열 각 요소를 다른 행에 출력하고 싶기에 printf
내장 함수를 사용합니다:
printf "+ %s\n" ${fruits[*]}
# + Apple
# + Desert
# + fig
# + Plum
음... Desert
와 fig
가 왜 다른 행에 출력이 되는 걸까요? 따옴표를 사용해봅시다:
printf "+ %s\n" "${fruits[*]}"
# + Apple Desert fig Plum
이제는 전부 한 행에 출력이 되는군요! 이제 ${fruits[@]}
차례입니다:
printf "+ %s\n" "${fruits[@]}"
# + Apple
# + Desert fig
# + Plum
큰따옴표 내에서 배열 안에 있는 각 요소들은 다른 인수로 ${fruits[@]}
출력됩니다. 여기서 배열 요소에 포함된 공백은 유지됩니다.
또한 slice 연산자를 사용하여 배열 슬라이스할 수 있습니다:
echo ${fruits[@]:0:2} # Apple Desert fig
이 예제는 ${fruits[@]}
는 배열 전체를 나타내고, :0:2
는 인덱스 0에서 시작해서 2번째 값까지 슬라이스하여 출력하라는 의미입니다.
배열에 요소를 추가하는 방법은 매우 간단합니다. 여러 값을 같이 할당하는 경우에는 더 유용합니다. 우리는 다음처럼 사용할 수 있습니다:
fruits=(Orange "${fruits[@]}" Banana Cherry)
echo ${fruits[@]} # Orange Apple Desert fig Plum Banana Cherry
위 예제는 ${fruits[@]}
로 모든 배열 값들을 불러오고, 추가된 내용과 같이 새로운 배열을 생성한 다음 fruits
변수에 할당합니다.
배열에서 요소를 제외하려면 unset
명령을 사용합니다:
unset fruits[0]
echo ${fruits[@]} # Apple Desert fig Plum Banana Cherry
Bash는 다른 프로그램들과 출력을 조작하기 위한 강력한 도구를 가지고 있습니다. Stream을 사용하여 다른 프로그램이나 파일에 프로그램 출력을 전송하여 로그를 작성하거나 원하는 어떤 작업이든 할 수 있습니다.
Pipe는 컨베이어를 만들어 명령 실행을 제어할 수 있도록 합니다.
이 강력하고 정교한 도구를 사용하는 방법을 이해하는 것이 매우 중요합니다.
Bash는 입력을 받아 차례로 출력을 보내거나 문자 streams 합니다. 이런 stream은 파일이나 다른 것으로 리다이렉션 될 수 있습니다.
여기 3개 서술자가 있습니다:
Code | Descriptor | 설명 |
---|---|---|
0 |
stdin |
표준 입력. |
1 |
stdout |
준 출력. |
2 |
stderr |
오류 출력. |
리디렉션은 명령에를 대한 출력값이 어디로 갈 것인가, 명령에 대한 입력값이 어디서 올 것인가에 대한 제어를 가능하게 해줍니다. Stream에서 리디렉션을위해 다음과 같은 연산자를 사용하고 있습니다:
연산자 | 설명 |
---|---|
> |
출력 전송 |
&> |
출력과 오류 출력 전송 |
&>> |
출력과 오류 출력을 전송지에 추가 |
< |
입력 전송 |
<< |
요 문서 syntax |
<<< |
여기 문자열 |
리디렉션을 사용하는 예제 조금:
# output of ls will be written to list.txt
ls -l > list.txt
# append output to list.txt
ls -a >> list.txt
# all errors will be written to errors.txt
grep da * 2> errors.txt
# read from errors.txt
less < errors.txt
파일뿐만 아니라 다른 프로그램에서도 표준 스트림 리디렉션을 할 수 있습니다. Pipes를 이용하여 프로그램 출력을 다른 곳 입력으로 사용할 수 있습니다.
다음 예시에서는 command1
출력을 command2
로 보내고, 그 출력을 다시 command3
입력으로 보냅니다:
command1 | command2 | command3
이런 구조를 파이프라인 이라고 부릅니다.
사실 몇 가지 프로그램을 통해서 데이터를 처리하는 데 사용할 수 있습니다. 예를 들어 ls -l
로 출력한 값에서 grep
명령을 이용하여 .md
확장자를 가진 파일만 출력한 뒤 less
프로그램으로 출력해줍니다:
ls -l | grep .md$ | less
파이프라인에서 종료 상태는 일반적으로 마지막 명령이 끝나는 시점입니다. 파이프라인에서 작동 중인 모든 명령이 완료될 때까지 셸 상태를 반환하지 않습니다. 파이프라인으로 연결된 명령 중 하나가 실패하였을 때, 연결된 파이프라인이 모두 실패로 설정하고 싶다면 다음과 같이 pipefail 옵션을 설정해야 합니다:
set -o pipefail
명령어 나열은 ;
, &
, &&
, ||
연산자를 이용하여 하나 이상 파이프라인에 대한 순서를 나타냅니다.
명령이 제어 연산자 &
에 의해서 종료될 경우, 셸은 서브 셸에서 비동기적으로 명령을 실행합니다. 다른 말로 하자면, 명령이 백그라운드로 실행됩니다.
;
로 구분된 명령은 차례로 실행됩니다: 차례로. 셸은 각 명령이 종료될때까지 기다립니다.
# command2 will be executed after command1
command1 ; command2
# which is the same as
command1
command2
&&
와 ||
로 구분된 명령어 나열은 각각 _AND_와 OR 목록이라고 합니다.
_AND_list_는 다음과 같습니다:
# command2 will be executed if, and only if, command1 finishes successfully (returns 0 exit status)
command1 && command2
_OR-list_는 다음과 같습니다:
# command2 will be executed if, and only if, command1 finishes unsuccessfully (returns code of error)
command1 || command2
AND 또는 OR 명령어 나열에서 반환 코드는 마지막으로 실행한 명령에 대한 종료 상태입니다.
다른 언어들과 마찬가지로 Bash 조건문은 어떤 작업을 수행할지에 대해서 결정합니다. 결과는 [[ ]]
로 묶어야지만 표현을 평가하여 결정됩니다.
조건식은 &&
와 ||
연산자를 이용하여 _AND_와 _OR_로 사용할 수 있습니다. 이외 다양한 표현을 이용하여 확인할 수 있습니다.
두 가지 조건문이 있습니다: if
문과 case
문.
[[ ]]
(또는 sh
에서는 [ ]
) 안에 있는 식은 테스트 명령이나 primaries라 부릅니다. 이 수식들은 조건에 대한 결과를 보여줄 수 있도록 도와줍니다. 다음 테이블에서는 sh
에서 사용할 수 있도록 [ ]
를 이용하여 나타냈습니다. 여기서 bash에서 이중 대괄호와 대괄호의 차이에 대해서 이야기합니다.
파일 시스템에서 작동:
Primary | Meaning |
---|---|
[ -e FILE ] |
FILE 이 존재하면(exist), True |
[ -f FILE ] |
FILE 이 존재하고 일반 파일(regular file)이면, True. |
[ -d FILE ] |
FILE 이 존재하고 디렉토리(directory)이면, True. |
[ -s FILE ] |
FILE 이 존재하고 비어있다(size more than 0)면, True. |
[ -r FILE ] |
FILE 이 존재하고 읽기가능하다(readable)면, True. |
[ -w FILE ] |
FILE 이 존재하고 쓰기가능하다(writable)면, True. |
[ -x FILE ] |
FILE 이 존재하고 실행가능하다(xecutable)면, True. |
[ -L FILE ] |
FILE 이 존재하고 싱볼릭 링크(symbolic link)이면, True. |
[ FILE1 -nt FILE2 ] |
FILE1이 FILE2 보다 새로운 파일입니다. (FILE1 is newer than FILE2.) |
[ FILE1 -ot FILE2 ] |
FILE1이 FILE2 보다 오래된 파일입니다. (FILE1 is older than FILE2.) |
문자열에서 작동:
Primary | Meaning |
---|---|
[ -z STR ] |
STR 이 비었음 (길이가 0 (zero)). |
[ -n STR ] |
STR 이 비어있지 않음 (길이가 0 아님 (non-zero)) |
[ STR1 == STR2 ] |
STR1 과 STR2 가 같음. |
[ STR1 != STR2 ] |
STR1 과 STR2 가 같지 않음. |
산술 이헝 연산자:
Primary | Meaning |
---|---|
[ ARG1 -eq ARG2 ] |
ARG1 는 ARG2 와 같다 (equal). |
[ ARG1 -ne ARG2 ] |
ARG1 는 ARG2 와 같지 않다 (not equal). |
[ ARG1 -lt ARG2 ] |
ARG1 는 ARG2 보다 작다 (less then). |
[ ARG1 -le ARG2 ] |
ARG1 는 ARG2 보다 작거나 같다 (less than or equal). |
[ ARG1 -gt ARG2 ] |
ARG1 는 ARG2 보다 크다 (greater than). |
[ ARG1 -ge ARG2 ] |
ARG1 는 ARG2 보다 크거나 같다 (greater than or equal). |
조건을 combining expressions를 사용하여 결합할 수 있습니다:
Operation | Effect |
---|---|
[ ! EXPR ] |
EXPR 이 거짓이면 True. |
[ (EXPR) ] |
EXPR 값 반환. |
[ EXPR1 -a EXPR2 ] |
논리 AND. EXPR1 와(and) EXPR2 조건이라면 True. |
[ EXPR1 -o EXPR2 ] |
논리 OR. EXPR1 또는(or) EXPR2 조건이라면 True. |
물론 더 유용한 primaries가 존재하며 Bash man pages에서 더 쉽게 찾아볼 수 있습니다.
if
문은 다른 프로그래밍 언어와 같이 작동합니다. 괄호 안에 있는 표현식이 참이면 then
과 fi
사이 코드가 실행됩니다. fi
는 조건부로 실행되는 코드 끝을 나타냅니다.
# Single-line
if [[ 1 -eq 1 ]]; then echo "true"; fi
# Multi-line
if [[ 1 -eq 1 ]]; then
echo "true"
fi
마찬가지로 if..else
문을 사용할 수 있습니다:
# Single-line
if [[ 2 -ne 1 ]]; then echo "true"; else echo "false"; fi
# Multi-line
if [[ 2 -ne 1 ]]; then
echo "true"
else
echo "false"
fi
때로는 if..else
문이 우리가 원하는 만큼 충분한 기능을 하지 못할 수도 있습니다. 그럴 경우, if..elif..else
문을 사용할 수 있습니다.
다음 예제를 봅시다:
if [[ `uname` == "Adam" ]]; then
echo "Do not eat an apple!"
elif [[ `uname` == "Eva" ]]; then
echo "Do not take an apple!"
else
echo "Apples are delicious!"
fi
들어온 값에 맞는 작업을 진행할 때, 중첩된 if
문보다 유용한 case
문을 이용할 수 있습니다. 더 복잡한 조건일 때, case
문을 다음과 같이 사용합니다:
case "$extension" in
"jpg"|"jpeg")
echo "It's image with jpeg extension."
;;
"png")
echo "It's image with png extension."
;;
"gif")
echo "Oh, it's a giphy!"
;;
*)
echo "Woops! It's not image!"
;;
esac
각 case는 패턴과 일치하는 식입니다. |
기호는 여러 패턴을 분리하는 데 사용되며, )
연산자는 패턴 목록 끝을 나타냅니다. 첫 번째 명령과 맞는다면 실행됩니다. *
는 정의된 패턴에 일치하지 않는 것들을 나타내기 위한 패턴입니다. 명령에 대한 각 블록은 ;;
연산자를 이용하여 분리합니다.
더는 놀라지 않겠죠. 다른 프로그래밍 언어들처럼 bash에서 사용되는 반복문은 주건 제어문이 true일 때 반복 처리 코드 블록을 진행합니다.
Bash에서 반복문은 4가지 형식이 있습니다: for
, while
, until
, select
.
for
문은 C 족과 매우 비슷합니다. 다음과 같이 사용합니다:
for arg in elem1 elem2 ... elemN
do
# statements
done
각 루프를 통과할 때, arg
에 elem1
에서 elemN
까지의 값이 할당됩니다. 와일드카드 또는 중괄호 expansions로 값이어도 됩니다.
또한, 한 줄로 for
문을 작성해야 한다면 다음처럼 do
앞에 세미콜론을 붙여서 사용할 수 있습니다:
for i in {1..5}; do echo $i; done
for..in..do
형식이 이상하다면 다른 방법으로 C와 같은 스타일로 for
문을 사용할 수 있습니다:
for (( i = 0; i < 10; i++ )); do
echo $i
done
디렉터리에서 각 파일에 같은 작업을 진행하려고 할 때 for
문을 사용하면 편합니다. 예를 들어, 모든 .bash
파일을 script
폴더로 옮기고 실행 권한을 준다면, 다음과 같이 작성할 수 있습니다:
#!/bin/bash
for FILE in $HOME/*.bash; do
mv "$FILE" "${HOME}/scripts"
chmod +x "${HOME}/scripts/${FILE}"
done
while
반복문은 조건을 테스트하고 _true_가 될 때까지 명령을 반복합니다. 조건은 if..then
조건에서 primary 사용되는 것과 다르지 않습니다. 그래서 while
반복문은 다음과 같습니다:
while [[ condition ]]
do
# statements
done
for
반복문과 같이 같은 줄에 do
를 사용한다면 앞에 세미콜론을 붙여서 사용해야 합니다.
예제는 다음과 같습니다:
#!/bin/bash
# Squares of numbers from 0 through 9
x=0
while [[ $x -lt 10 ]]; do # value of x is less than 10
echo $(( x * x ))
x=$(( x + 1 )) # increase x
done
until
반복문은 while
반복문과는 정반대로 작동합니다. while
과 같이 테스트 조건을 확인하고 그 조건이 false 될 때까지 작동합니다:
until [[ condition ]]; do
#statements
done
select
반복문은 사용자 매뉴얼을 구성할 수 있도록 도와줍니다. for
반복문과 같은 구문으로 되어있습니다:
select answer in elem1 elem2 ... elemN
do
# statements
done
select
은 사용자가 입력하면, 연속된 숫자들과 스크린에 모든 elem1..elemN
을 출력합니다. 일반적으로 $?
(PS3
값)과 같이 보입니다. 입력한 대답은 answer
에 저장됩니다. answer
가 1..N
사이 수인 경우, statements
가 실행되고 select
는 다음을 실행합니다. 여기서 break
문을 사용할 수 있습니다.
작동 예제는 다음과 같습니다:
#!/bin/bash
PS3="Choose the package manager: "
select ITEM in bower npm gem pip
do
echo -n "Enter the package name: " && read PACKAGE
case $ITEM in
bower) bower install $PACKAGE ;;
npm) npm install $PACKAGE ;;
gem) gem install $PACKAGE ;;
pip) pip install $PACKAGE ;;
esac
break # avoid infinite loop
done
이 예제에서는 사용자에게 사용할 어떤 패키지 관리자 {s,he}를 묻습니다. 그리고 패키지 관리자를 선택하고 설치할 패키지를 묻습니다.
실행하면 다음과 같은 명령을 확인할 수 있습니다:
$ ./my_script
1) bower
2) npm
3) gem
4) pip
Choose the package manager: 2
Enter the package name: bash-handbook
<installing bash-handbook>
반복문이 일반적으로 끝내거나 특정 단계 전에 멈출 때가 필요합니다. 이런 경우엔 셸 내장 함수인 break
과 continue
문을 사용할 수 있습니다. 둘 다 모든 반복문에서 사용할 수 있습니다.
break
문은 끝나기 전 현재 반복문에서 나갈 때 사용합니다. 이미 사용 가능합니다.
continue
문은 한단계를 넘어서 진행됩니다. 다음과 같이 사용할 수 있습니다:
for (( i = 0; i < 10; i++ )); do
if [[ $(( i % 2 )) -eq 0 ]]; then continue; fi
echo $i
done
위 예제를 실행한다면, 0에서 9까지의 모든 짝수를 출력합니다.
스크립트에서 함수를 정의하고 불러 사용 가능 합니다. 다른 프로그래밍 언어처럼 Bash에서 함수는 코드 덩어리지만 차이가 있습니다.
Bash에서 함수는 함수 _이름_으로 그룹화한 명령입니다. 함수를 호출하는 방법은 다른 프로그램과 같이 호출하며, 그냥 이름을 사용하면 함수가 호출됩니다.
다음과 같이 함수를 선언할 수 있습니다:
my_func () {
# statements
}
my_func # call my_func
함수를 호출하기 전 선언을 하여야지 사용할 수 있습니다.
함수는 인수를 취해 결과값을 반환할 수 있습니다 -- 종료 코드. 인수는 함수내 non-interactive 모드에서 스크립트에 주어진 인수와 같이 취급됩니다. -- positional parameters를 사용해서. 결과 코드는 return
명령을 사용하여 반환합니다.
함수가 정상적으로 실행된 것을 나타내는 이름을 가지고 0
을 반환합니다.
# function with params
greeting () {
if [[ -n $1 ]]; then
echo "Hello, $1!"
else
echo "Hello, unknown!"
fi
return 0
}
greeting Denys # Hello, Denys!
greeting # Hello, unknown!
이미 종료 코드에 대해서 이야기하였습니다. return
명령은 인수 없이 마지막으로 실행한 명령에 대한 종료 코드를 반환합니다. 그리고 return 0
으로 구성해 두면 정상 종료 코드로 0
을 반환합니다.
셸에서도 스크립트를 디버깅할 수 있는 툴을 제공합니다. 디버그 모드로 실행하려면 스크립트 shebang에 특별한 옵션을 추가해야 합니다:
#!/bin/bash options
이 옵션은 셸 동작을 변경하는 설정입니다. 다음 표에서 도움이 되는 것들에 관해 설명해뒀습니다:
Short | Name | 설명 |
---|---|---|
-f |
noglob | 파일 이름 확장 (globbing)을 비활성화합니다. |
-i |
interactive | 스크립트를 interactive 모드로 작동합니다. |
-n |
noexec | 명령을 읽습니다만 실행하지는 않습니다 (문법 체크). |
pipefail | 어떤 명령이 제대로 실행되지 못하여 마지막 명령이 실패한 것이 아니라면, 파이프라인 오류를 확인합니다. | |
-t |
— | 첫 명령 후 종료합니다. |
-v |
verbose | 각 명령을 실행하기 전 stderr 를 출력합니다. |
-x |
xtrace | 각 명령을 실행하기 전 명령과 stderr 에 확장 변수를 출력합니다. |
예를 들어 다음과 같이 -x
옵션을 사용해 스크립트를 만듭니다:
#!/bin/bash -x
for (( i = 0; i < 3; i++ )); do
echo $i
done
이 스크립트를 실행하게되면 다른 정보들과 함께 stdout
변숫값을 출력합니다:
$ ./my_script
+ (( i = 0 ))
+ (( i < 3 ))
+ echo 0
0
+ (( i++ ))
+ (( i < 3 ))
+ echo 1
1
+ (( i++ ))
+ (( i < 3 ))
+ echo 2
2
+ (( i++ ))
+ (( i < 3 ))
때때로 스크립트 일부를 디버깅 해야 되는 경우가 발생합니다. 이때 set
명령을 사용하면 됩니다. 이 명령으로 옵션을 활성화하고 비활성화할 수 있습니다. 사용법은 -
을 사용하여 활성화 시키고, +
를 사용하여 비활성화합니다:
#!/bin/bash
echo "xtrace is turned off"
set -x
echo "xtrace is enabled"
set +x
echo "xtrace is turned off again"
나는 이 작은 핸드북이 재미있고 도움이 되었으면 합니다. 나 자신이 Bash에 대한 기본을 잊지 않기 위해 이 핸드북을 작성하였습니다. 간결하고 의미 있는 것을 기록하려 하였습니다만, 잘 안되었다면 이해해 주셨으면 합니다.
이 핸드북은 bash에 대한 내 경험을 이야기합니다. 이것은 그렇게 포괄적으로 서술되지 않았기에 더 많은 것을 원한다면 man bash
를 실행해서 확인하십시오.
컨트리뷰션은 절대적으로 환영하며 어떤 질문이나 이슈에 대해 나에게 알려주십시오. 이슈를 만들어서 말이죠.
이 핸드북을 읽어 주셔서 감사합니다!
여기는 Bash에 대해서 더 알 수 있는 문서 목록입니다:
- Bash man 페이지. Bash가 실행되는 여러 환경에서 도움말 시스템
man
을 사용하여 Bash에 관한 내용을 확인할 수 있으며, 실행 명령어는man bash
입니다.man
명령에 대한 더 많은 내용을 확인하고 싶으면 The Linux Information Project에서 호스팅 되고 있는 "The man Command" 웹페이지를 확인하십시오. - "Bourne-Again SHell manual"은 많은 형식 (HTML, Info, TeX, PDF, Texinfo)으로 되어있습니다. https://www.gnu.org/에서 호스팅 되고 있습니다. 2016년 1월 현재 버전 4.3이며, 마지막 업데이트는 2015년 2월 2일입니다.
- awesome-bash는 Bash 스크립트와 리소스에대한 모음
- awesome-shell는 셸 리소스에 대한 다른 모음
- bash-it 일상 업무에서 사용할 셸 스크립트와 개인화된 명령을 사용하여 개발과 관리를 위한 강력한 프레임워크를 제공합니다.
- dotfiles.github.io는 다양한 dotfile 컬렉션과 bash와 다른 셸에서 사용할 수 있는 셸 프레임워크를 소개하는 곳입니다.
- learnyoubash는 첫 bash 스크립트를 작성하는 데 도움이 되는 곳입니다.
- shellcheck는 셸 스크립트에 대한 정적 분석 도구입니다. www.shellcheck.net 웹페이지로 사용하거나 명령어를 사용할 수 있습니다. 설치에 대한 자세한 내용은 koalaman/shellcheck에서 확인할 수 있습니다.
마지막으로, Stack Overflow에서 tagged as bash는 많은 질문에 대한 답을 얻을 수 있으며, 그것을 통해서 배울 수 있는 가장 좋은 장소입니다.