나는 Ubuntu 환경에서의 bash Shell 이다.
알아보자
셀 스크립팅의 기본은 간단하다
아래 포맷만 지키면 된다.
(prompt) command { option } { arg.. }
여러가지 환경변수들은 미리 지정되어 있다.
실제 값의 확인은 echo ${name} 으로 확인하며
미리 지정된 여러가지 값들은 아래와 같다.
변수 | 설명 | 변수 | 설명 |
HOME | 현재 사용자 홈 디렉터리 | PATH | 실행 파일을 찾는 디렉터리 |
LANG | 기본 지원 언어 | PWD | 사용자 현재 디렉터리 |
TERM | 로그인 터미널 타입 | SHELL | 로그인해서 사용하는 셸 |
USER | 현재 사용자의 이름 | DISPLAY | X 디스플레이 이름 |
COLUMNS | 현재 터미널의 컬럼 수 | LINES | 현재 터미널의 라인 수 |
PS1 | 1차 명령 프롬프트 변수 | PS2 | 2차 명령 프롬프트(대개 '>') |
BASH | bash 셸의 경로 | BASH_VERSION | bash 버전 |
HISTFILE | 히스토리 파일 경로 | HISTSIZE | 히스토리 파일의 저장 개수 |
HOSTNAME | 호스트이름 | USERNAME | 현재 사용자 이름 |
LOGNAME | 로그인 이름 | LS_COLORS | ls 명령의 확장자 색상 옵션 |
메일을 보관하는 경로 | OSTYPE | OS 타입 |
더 있지만.. 나중에 더 알아보도록 하자.
환경변수의 변경은 export {name}={value}로 실행하면 바뀐다.
printenv 커맨드로 출력할 수 있다.
**일부 환경변수는 출력되지 않는다.
셸 스크립트 작성 어렵지 않다.
sample.sh
#!/bin/sh => bash를 사용하고 셸 스크립트를 시작하겠다는 의미다. 무조건 써준다.(#! 무조건 써준다)
echo "name: " $USER
echo "home: " $HOME
exit 0 => 종료 코드를 반화하는 것이 올바른 결과가 나왔는지 알 수 있는 방법이다.
위 파일을 만들었다면 이제 실행해야 한다.
스크립트 파일은 크게 2가지 방법으로 실행된다.
1. sh 명령으로 실행
$ sh sample.sh
2. 실행 가능 속성으로 바꾸어 실행
** 해당 파일은 chmod를 바꾸어서 x (execute)를 추가한 뒤에 사용 가능하다.
./sample.sh
./{script_file_name}으로 알면 되겠다.
여기서 ./ 가 무엇을 의미하는 지 알아보자
우선 . 은 현재 디렉터리를 의미한다.
다시 말해서 ./은 현재 디렉터리에서의 파일인 sample.sh를 실행하라는 것이다.
아니.. 굳이 써야하나..?라고 할 수 있는데
일반적으로 경로를 설정하지 않으면 셸에서는 $PATH에 있는 디렉터리만 찾아보기 때문에
파일을 찾을 수 없는 경우가 있다.
즉, 제대로 파일을 실행하기 위해서는 경로를 지정해주는 것이 필요하다.
그럼 셸 스크립트에서 사용하는 변수에 대해서 알아보자
변수는 다른 프로그래밍언어와 같다.
계속 쓸려면 그대로 리터럴 값을 쓰느니, 저장해서 쓴다.
셸 스크립트에서도 자주 쓰면 변수로 선언해서 쓴다.
하지만 다른 프로그래밍 언어와 다른 기본 규칙이 있다.
1. 셸 스크립트에서는 변수를 사용하기 전 미리 선언하지 않는다.
2. 변수에 값이 할당되면 자동 생성된다.
3. 변수에 넣는 모든 값은 문자열로 취급한다.
4. 변수 이름은 대소문자를 구분한다.
5. 변수를 대입할 때는 = 좌우에 공백이 없어야 한다. => 이것이 진짜 특이하다.
test1 = Hello => 공백이 있으면 안됨
test2=Hello => 정상
test3=heloo threr => 띄어쓰기를 넣으려면 ""로 선언
test4="heloo there" => 정상
test5=7+5 => 12가 아니라 7+5가 저장됨
변수를 저렇게 선언하는 것은 배웠고
변수를 출력하는 것은 $를 붙여서 한다.
** $ 자체를 출력하고 싶을 때는 이스케이프 문자인 ' \ ' 를 넣어서 출력한다.
**일반적으로 ${name} 과 "${name}" 은 똑같이 취급되는데
공백으로 인한 오류를 막기 위해서는 ""를 사용해서 변수를 쓰는 것도 나쁘지 않다.
변수의 모든 것은 문자열로 인식한다고 했는데 그럼 변수의 연산은 없는걸까??
그렇지는 않다.
사칙연산? 할 수 있다. 다만 몇가지 규칙이 존재한다.
expr 키워드를 사용하고 `라는 역따옴표를 사용하는 것이다.
또한 연산식에서 괄호를 쓰고싶거나 문자를 쓸 때는 이스케이프 코드가 필요하다. [ * 과 ( ) 에 필요하다. ]
#!/bin/sh
num1=100
num2=$num1+200
echo $num2
num3=`expr $num1 + 200 ` => 이건 숫자 1 옆에 있는 역따옴표를 쓴다.
echo $num3
num4=`expr \( $num1 + 200 \) / 10 \* 2` => (num1 + 100)/10*2
파라미터 변수라는 것도 존재한다.
$0, $1, $2 .. 등이 그 예이다.
??? 그게 뭐야???
예를 들어서
apt -y install gftp 를 실행한다고 해보자.
파라미터 변수는 이렇다.
$0 | $1 | $2 | $3 |
apt | -y | install | gftp |
??? 그냥 커맨드 키워드 순서대로 파라미터 변수가 되는건가??
맞다. 명령어에 포함된 모든 키워드들을 $변수 형식으로 지정하는 것이다.
어떤 식으로 쓰냐하면..?
아래 파일을 실행시킬 때 조금 다르다.
paravar.sh
#!/bin/sh
echo $0
echo $1
echo $2
echo $*
exit 0
아래처럼 실행하면 결과가 나온다
sh paravar.sh 값1 값2 값3 => 실핼할 때 이렇게 한다.
값1
값2
값1 값2 값3
그다음에는 if 문과 case 문에 대해서 알아보자
역시 분기문이 없는 프로그래밍을 상상할 수는 없다.
하지만 형식이 조금 다르다.
if [ condition ]
then
참일 때 실행
fi
??? 뭐가 다르긴 허다..?
예를 통해 알아보자
아래 파일을 실행하면 결과는 어떻게 나올까?
#!/bin/sh
if [ "abc" = "abc" ]
then
echo "True"
fi
exit 0
True라는 결과를 얻을 수 있다.
**그리고 if문 안에 조건에 들어가는 모든 단어들은 띄어쓰기가 되어야 한다.
[띄고"abc" 띄고 = 띄고 "abc" 띄고] 이렇게다.
?? 근데 else 문은 없어??
있다. 당연히 있다.
아래가 끝이다. 뭐 특별한 것은 없으니 넘어가자
if [ condition ]
then
참일 때 실행
else
거짓일 때 실행
fi
**참고로 조건문에 들어가는 비교연산자와 논리연산자는 조금 다르게 생겨서 외워야 한다.
"abc" = "abc" | 두 문자열이 같으면 참 |
"abc" != "abc" | 두 문자열이 다르면 참 |
-n "abc" | 문자열이 NULL이 아니면 참 |
-z "abc" | 문자열이 NULL 이면 참 |
수식1 -eq 수식2 | 두 수식의 값이 같으면 참 |
수식1 -ne 수식2 | 두 수식의 값이 다르면 참 |
수식1 -gt 수식2 | 수식1이 크면 참 |
수식1 -ge 수식2 | 수식1이 크거나 같으면 참 |
수식1 -lt 수식2 | 수식1이 작으면 참 |
수식1 -le 수식2 | 수식1이 작거나 같으면 참 |
!수식 | 수식이 거짓이면 참 |
또한 if 문에서 다른 조건들도 집어넣을 수가 있는데 바로 파일과 관련된 조건이다.
-d 파일이름 | 파일이 디렉터리면 참 |
-e 파일이름 | 파일이 존재하면 참 |
-f 파일이름 | 파일이 일반 파일이면 참 |
-g 파일이름 | 파일에 set-group-id 가 설정되면 참 |
-r 파일이름 | 파일이 읽기 가능이면 참 |
-s 파일이름 | 파일 크기가 0이 아니면 참 |
-u 파일이름 | 파일에 set-user-id가 설정되면 참 |
-w 파일이름 | 파일이 쓰기 가능이면 참 |
-x 파일이름 | 파일이 실행 가능이면 참 |
또한 조건문에 빠지지 않는 case문도 존재한다.
if를 계속 쓰는 것은 비효율적이니까
당연히 case도 있어야 한다.
근데 case문도 형식이 완전 달라서 조금 외워야 하는 부분이 존재한다.
이것도 예를 들면 바로 이해가 된다.
#!/bin/bash
case "$1" in
start)
echo "start";;
stop)
echo "stop";;
restart)
echo "restart";;
*)
echo "***";;
esac
exit 0
위의 예시는 $1 (파라미터 변수) 에 따라 4가지 분기가 있는 것이다.
**case 문의 실행부분에서는 세미콜론 2개를 붙여써야 한다는 특징이 있다.
우리가 case 문 종료할 때는 그냥 괄호만 닫지만 여기서는
esac 라는 case를 거꾸로 쓴 키워드를 사용해서 case 문이 끝난 것을 알 수 있다.
다른 예도 한 번 보자
#!/bin/sh
echo "yes or no"
read answer
case $answer in
yes | y | Yes | YES)
echo "YEAH"
echo "GOOD";;
[nN]*)
echo "Noooh";;
*)
echo "what?";;
exit 1
esac
exit 0
조건에 들어가는 ' ) ' 왼쪽을 보면 알다시피 정규표현식도 있고 뭐 논리연산자도 있고 그렇다.
이렇게 유용하게 쓸 수 있다.
AND나 OR 같은 연산자는 다르게 사용할 수도 있다.
and는 -a 나 &&로 대신 사용할 수 있고
or는 -o나 || 로 대신 사용할 수 있다.
다만 -a 나 -o 같은 경우는 [ ] 안에서도 사용할 수 있는데
이 때 과호 등의 특수 문자 앞에서는 이스케이프 코드( \ )를 붙여서 써야 한다.
#!/bin/sh
echo "whatever"
read fname => 파일이름을 입력받아서 아래 문장을 실행
if [ -f $fname ] && [ -s $fname ] ; then => 일반 파일이면서 크기가 0이 아니라면 then 실행
head -5 $fname
else
echo "NO FILE"
fi
exit0
그다음 반복문에 대해서도 알아보자
알다시피 for문을 쓴다.다만 for문에 들어가는 조건문이 조금 다르다.
for {var} in {value...}
do
{expression}
done
그니까 변수와 값을 넣고 변수를 expression 에서 이용하게 된다.
예를 들어보자
#!/bin/sh
hap=0
for i in 1 2 3 4 5 6 7 8 9 10
do
hap=`expr $hap + $i`
done
echo $hap
exit 0
보다시피 1부터 10까지의 합이 출력된다.
사칙연산을 하기 위해 expr 키워드를 이용한 것을 알 수 있다.
이거 말고도 다른 반복문이 있었는데..?
바로 while 문이다.
역시 있다.
조건식이 참이라면 계속 반복하는 것? 셸에도 있다.
#!/bin/sh
while [ 1 ]
do
echo "good"
done
exit 0
꺼지지 않는 반복문과 비슷해보이는데??
맞다.
어 근데.. shell에는 우리가 안쓰는 다른 문장이 하나 더 있다.
바로 until 문이다.
~ 할 때까지 라는 영어 해석과 알맞다.
until은 조건식이 참이 될 때까지 반복하는 것이다.
즉, 거짓일 때 계속 반복한다.
굳이 왜 있냐 싶지만 그냥 알면 편리한 기능과 같다.
**이건 그냥 while 에서 until만 집어넣으면 되어서 예는 알아보지 않도록 하겠다.
또한 반복문을 탈출하거나 제어하는 키워드 역시 존재한다.
break, continue, exit, return 이 그 예다.
exit와 return만 셸스크립트에서 조금 다르다.
exit는 해당 프로그램 자체를 종료하는 것이고
return 은 함수 안에서 함수를 호출한 곳으로 돌아가게 하는 것이다.
사실 같은데.. 우리가 맨날 main에서 return 하면 프로그램이 종료되니까 return이 종료되는 줄 안다.
마지막으로 추가적으로 몇가지는 알아두자
1. 함수만들기
역시나 스크립트는 사용자 정의 함수가 있어야 한다.
셸 스크립트도 갖고 있다.
형식은 뭐 비슷하다.
{function name} () {
...
...
...
}
그냥 같다.
함수에 파라미터를 넣고 싶다??
{function name} () {
$1, $2 .....
...
...
}
//호출 시에만 아래에 파라미터를 넣어서 호출하면 된다.
{function name} {param1, param2, ...}
유용한 pre-defiend function이 많다.
1. eval => 문자열을 명령문으로 인식한다.
2. export => 외부 변수로 선언한다. (다른 프로그램에서도 이용가능)
3. printf => C의 printf랑 비슷하다. 포맷 맞추어서 출력 가능하다.
4. shift => 파라미터 변수를 왼쪽으로 한 단계씩 아래로 시프트 한다.
$2 였던게 $1이 되고 그런 것을 말하는 것이다.
또 참고해야 할 것이 있는데
**커맨드의 결과 자체를 사용하고 싶다면 $ 을 잘 이용해야 한다.
**결과를 다시 파라미터로 이용하고 싶다면 set을 잘 이용해야 한다.
예를 들어보자
#!/bin/sh
echo "오늘은 $(date)" => date 라는 커맨드가 실행된 것이 쓰이기 위해 $를 같이 씀
set $(date) => date의 결과를 파라미터로 $1,$2...로 저장함.
echo "오늘은 $4 요일" => 즉 $4가 date의 4번째 파라미터인 것을 알 수 있음
exit 0
이 정도면 Shell을 다룰 수 있다.
남은 것은 반복뿐이다.
그냥 끝내면 섭하니까
예를 들어 하나 예시를 들어볼까
서버 A의 파일을 서버 B에 보내는 스크립트를 만들어보자
필수적으로
1. SSH 설치가 되어 있어야함(#apt -y install openssh-server)
2. 서버 B의 계정과 비밀번호, IP를 알고 있어야함 (포트는 대개 22)
3. 해당 계정에 대해 파일 쓰기 권한이 있어야함
서버 A의 파일을 처리해서 B로 보내는 것을 해보자.
결론은 이거다.
#!/bin/sh
sed 's/10.1.2.3/10.1.2.4/g' before.txt | sed 's/test_world/svc_world/g' | sed 's/test_user/svc_user/g' |sed 's/mypassword/mypassword!@#/g' > after.txt
scp /root/test/after.txt ubuntu@192.168.111.200:/home/ubuntu/after.txt
sed 라는 것으로 해당 파일을 편집할 수 있고
scp라는 것으로 파일을 전송할 수 있다.
저 스크립트를 sh {name}하면 된다.
'DevOps > Shell' 카테고리의 다른 글
Shell Script - 4, escape character (0) | 2022.11.23 |
---|---|
Shell Script - 3, Wildcard (0) | 2022.11.23 |
Shell script 11, Tip (0) | 2022.11.22 |
Shell Script - 2, Variable(1) (0) | 2022.11.21 |
Shell Script - 1, 작성하기 (1) | 2022.11.21 |