- 산술 연산 expr, let
- while and until loop
- for-loop
- 과제
1. 산술 연산 expr, let
앞 포스트에 expr과 let의 차이를 간단하게 비교 설명해 놓았다.
조금 더 자세하게 차이를 비교해보자.
- type : 특정 명령어 유형을 확인하기 위한 명령어
type 명령을 써서 차이를 확인해보면 다음과 같다.
apple@lww:~/bin$ type expr
expr is /usr/bin/expr
apple@lww:~/bin$ type let
let is a shell builtin
let 은 bash shell에 내장된 명령이다.
expr은 command로 지원되는 명령이다.
let은 bash shell에 종속된 명령이기에 다른 shell로 접근하게 되면 let 명령은 사용할 수 없다.
그러므로 bash shell에 종속되지 않는 스크립트를 만드려면 expr을 사용하면 좋고 bash shell 환경에서만 사용되는 스크립트라면 let을 사용하면 된다.
expr
- 정수형 산술연산(+, -, *, /, %)
- 논리연산(&&, ||)
- 관계연산(=, !=, >, >=, <, <=)
expr 10 + 5
expr 10 - 5
expr 5 '*' 2 # 곱셈 연산을 위해서는 홑따옴표(') 혹은 백슬래시(\)를 붙여야 한다.
expr 25 '/' 5 # 나눗셈 연산을 위해서는 홑따옴표(') 혹은 백슬래시(\)를 붙여야 한다.
expr 25 % 4
x=5
expr $x > 4
expr $x = 8
sum=`expr $x + 10`
let
- 정수형 산술연산
- bit 연산(<<, >>, &, |)
- 논리연산(&&, ||)
- 단항연산(++, --, +=, -=)
let sum=x+5
let x++
let x+=1
((sum=x=5))
((x++))
((x-=))
let은 공백없이 '((연산))' 형태로 사용할 수도 있다.
2. while and until
while 과 until 둘 다 조건부 loop로써 조건에 따라 반복을 수행할지 말지 결정한다.
- while : while 다음의 command가 성공하는 동안 do~done 사이의 명령어를 반복 실행 (조건일치 시 실행)
- until : until 다음의 command가 성공할 때까지 do~done 사이의 명령어를 반복 실행 (조건불일치 시 실행)
while
while 사용 방법 :
while 조건명령어
do
반복명령어
done
while test $num -le 5
do
echo Number: $num
((num++))
done
until
until 사용 방법 :
until 조건명령어
do
반복명령어
done
until test $num -gt 5
do
echo Number: $num
((num++))
done
예제 - 1
유저를 생성하는 스크립트다.
while 문을 이용하여 사용자에게 입력받은 username이 /etc/passwd 파일에 존재하는 지 검증 후 존재하면 다시 입력, 존재하지 않으면 생성하도록 스크립트를 작성한다.
#!/bin/bash
# Description : Create a user account.
echo -n "New username : "
read username
while getent passwd $username &> /dev/null # '&>'는 표준 출력과 표준 에러를 모두 의미
do
echo "Sorry, that account &username is already taken. Please pick a different username."
echo -n "New username : "
read username
done
sudo useradd -m -s /bin/bash $username
getent 명령어는 데이터베이스에서 엔트리(항목)를 조회하는 데 사용되는 유틸리티다.
일반적으로 getent는 /etc/passwd, /etc/group, /etc/hosts 등과 같은 데이터베이스에서 정보를 검색하는 데 사용된다.
이 명령어는 사용자 정보, 그룹 정보, 호스트 정보 등을 확인하는 데 유용하다.
사용 방법 :
getent [데이터베이스 이름]
ex)
getent passwd [유저명] : /etc/passwd 파일에서 유저를 검색
getent 명령어는 /etc/nsswitch.conf 파일에 정의된 데이터베이스 소스를 사용하여 데이터를 조회한다.
이 파일은 시스템이 어떤 데이터 소스를 사용하여 정보를 가져오는지를 설정한다.
passwd 파일은 먼저는 파일에서 조회되며, 없으면 systemd에서 조회된다는 의미다.
잡설이 길었지만 위 스크립트를 실행하면 다음과 같은 결과가 나온다.
# 실행 결과
apple@lww:~/bin$ create-user.sh
New username : apple
Sorry, that account &username is already taken. Please pick a different username.
New username : orange
apple@lww:~/bin$ id orange
uid=1001(orange) gid=1001(orange) groups=1001(orange)
예제 - 2
이번에는 반대로 유저를 지우는 스크립트다.
위의 조건과 마찬가지로 스크립트를 작성하지만 이번에는 whlie이 아닌 until을 사용해서 스크립트를 작성한다.
#!/bin/bash
# Usage : remove-user.sh
# Description : Remove the user with the entered name.
echo -n "Enter the username to delete : "
read username
until getent passwd $username &> /dev/null
do
echo "'$username' does not exist. try different username."
echo -n "Input username : "
read username
done
sudo userdel -r $username
echo "Success."
# 실행 결과
apple@lww:~/bin$ remove-user.sh
Enter the username to delete : orange
Input username : orange
[sudo] password for apple:
userdel: orange mail spool (/var/mail/orange) not found
Success.
apple@lww:~/bin$ getent passwd orange
apple@lww:~/bin$
위와 같이 while 문이나 until을 사용하면 반복적인 작업이 필요할 때 유용하게 사용할 수 있다.
예제 - 3
실행하려는 의도에 따라 반복문과 조건문을 함께 사용할 수도 있다.
#!/bin/bash
num=0
while [ $num -lt 5 ]
do
echo "Number : $num"
((num++))
if [[ "$num" == '2' ]]; then
break
fi
done
# 실행 결과
Number : 0
Number : 1
break라는 명령을 통해 loop를 탈출할 수 있다.
프로그래밍 언어와 구성이 매우 비슷하다.
[[ 조건명령어 ]] 부분에서 [[ ]]는 test의 확장된 사용이다.
조건명령으로 정규 표현식을 넣어서 사용하는 등 기존의 test보다 더 많은 기능을 사용할 수 있다.
3. for - loop
주어진 list 만큼 do ~ done 사이의 명령어를 반복 실행한다.
사용 방법 :
for [변수] in [LIST]
do
반복명령어
done
예제 - 1
#!/bin/bash
for num in 1 2 3 4 5 # {1..5} 라고 입력해도 동일
do
echo "Number : $num"
done
# 실행 결과
Number : 1
Number : 2
Number : 3
Number : 4
Number : 5
예제 - 2
#!/bin/bash
for NUM in $(seq 10)
do
echo $NUM
done
# 실행 결과
1
2
3
4
5
6
7
8
9
10
seq 명령은 입력한 숫자만큼 순차적으로 출력한다.
예제 - 3
또한 for 문은 신기하게도 와일드카드 문자를 넣을 수도 있다.
[LIST]를 넣는 부분에 '*'를 넣으면 현재 디렉토리의 리스트를 변수에 하나씩 할당하여 결과를 출력하게 된다.
#!/bin/bash
for file in *
do
ls $file
done
# 실행 결과
backup-exit.sh
case-exam1.sh
case-exam2.sh
create-user.sh
encript-file.sh
for-exam1.sh
for-exam2.sh
for-exam3.sh
if-exam2.sh
if-exam3.sh
if-exam.sh
input-exam1.sh
input-exam2.sh
input-exam3.sh
input-exam4.sh
input-exam5.sh
parameter-exam1.sh
parameter-exam2.sh
parameter-exam3.sh
remove-user.sh
sample.sh
storeFileList.sh
test.sh
varUsage.sh
while-if.sh
*의 입력이 가능하다면 /etc/*와 같이 특정 디렉토리의 파일들을 리스트화하여 작업을 수행할 수도 있는 것이다.
예제 - 4
#!/bin/bash
if [ ! -d ~/backup ]
then
mkdir ~/backup
fi
for FILE in *
do
cp $FILE ~/backup/$FILE.old
done
# 실행 결과
apple@lww:~/bin$ ls
backup-exit.sh encript-file.sh for-exam4.sh input-exam1.sh input-exam5.sh remove-user.sh varUsage.sh
case-exam1.sh for-exam1.sh if-exam2.sh input-exam2.sh parameter-exam1.sh sample.sh while-if.sh
case-exam2.sh for-exam2.sh if-exam3.sh input-exam3.sh parameter-exam2.sh storeFileList.sh
create-user.sh for-exam3.sh if-exam.sh input-exam4.sh parameter-exam3.sh test.sh
apple@lww:~/bin$ ls ~/backup
ls: cannot access '/home/apple/backup': No such file or directory
apple@lww:~/bin$ for-exam4.sh
apple@lww:~/bin$ ls ~/backup
backup-exit.sh.old for-exam2.sh.old input-exam1.sh.old parameter-exam2.sh.old varUsage.sh.old
case-exam1.sh.old for-exam3.sh.old input-exam2.sh.old parameter-exam3.sh.old while-if.sh.old
case-exam2.sh.old for-exam4.sh.old input-exam3.sh.old remove-user.sh.old
create-user.sh.old if-exam2.sh.old input-exam4.sh.old sample.sh.old
encript-file.sh.old if-exam3.sh.old input-exam5.sh.old storeFileList.sh.old
for-exam1.sh.old if-exam.sh.old parameter-exam1.sh.old test.sh.old
원래는 홈디렉토리 밑에 backup이라는 디렉토리가 존재하지 않았지만 스크립트를 실행 후 디렉토리를 생성하며 현재 디렉토리의 파일들을 '[파일명].old'라는 이름을 붙여서 복사하게 된다.
그리고 스크립트에서 if 문의 조건명령에서 옵션 앞에 '!' 를 붙였는데 이는 부정의 의미로 디렉토리가 아니면 참이 된다는 의미다.
4. 과제
작업 디렉토리를 입력받아 해당 디렉토리에 파일의 수와 디렉토리 수를 출력하는 프로그램을 작성하라.
아래와 같이 결과가 나오도록 스크립트를 작성
직접해보고 싶으면 아래 코드는 나중에 확인
예상 답안 - 1
#!/bin/bash
# Usage : count-file-dir.sh
# Description : Receive the working directory as input and output the number of files and directories in that directory.
echo -n Input a directory name :
read dirName
echo "================================"
fileCount=0
dirCount=0
for file in $dirName/*
do
if [[ -f "$file" && ! -h "$file" ]]; then
((fileCount++))
elif [[ -d $file ]]; then
((dirCount++))
fi
done
echo "$dirName :"
echo "echo files : $fileCount"
echo "echo dirs : $dirCount"
echo "================================"
echo
위 스크립트에서는 if 문에서 elif 도 활용해서 만든 것이다.
if 의 조건 명령으로 준 'test -h' 는 심볼릭 링크 파일이면 참을 의미하는 것으로 앞에 '!' 를 붙임으로써 심볼릭 링크가 아닌 파일을 찾으라는 명령이 된다.
예상 답안 - 2
#!/bin/bash
echo -n "Input a directory name: "
read dirname
if [ -d $dirname ];then
echo -e "------------------------------------------\n"
echo $dirname :
echo "echo files : $(ls -l $dirname | grep ^d | wc -l)"
echo "echo dirs : $(ls -l $dirname | grep ^- | wc -l)"
echo -e "------------------------------------------\n"
else
echo "$dirname does not exist."
exit 1
fi
예상 답안 2는 다른 사람이 작성한 코드를 참고해서 만들었다.
여기서는 for문을 사용하지 않고 if 문 만으로 작성했다.
예상 답안 2에서 'grep ^d' 부분의 해석은 출력의 첫번째 글자가 'd' 인 행을 찾아라는 것이다.
'^' 라는 문자는 정규 표현식에서 줄의 시작을 나타낸다.
일반적으로 'ls -l' 명령을 사용하게 되면 행의 제일 첫번째 글자는 해당 파일의 유형을 이니셜로 나타낸 것으로 '-' 는 일반 파일 'd' 는 디렉토리를 의미하며 'l' 은 심볼릭 링크 파일을 의미한다.
그러므로 'grep ^d' 를 통해 디렉토리만을 추출해서 카운트할 수 있는 것이다.
스크립트가 위와 다르더라도 정답은 정해져있지 않으며 어떤 디렉토리를 입력하든 결과만 잘 출력된다면 성공이다.
아래 영상을 참고했습니다.
https://youtube.com/playlist?list=PLApuRlvrZKog2XlvGJQh9KY8ePCvUG7Je&si=_xi5eXMNkaFV6eR-
'Shell Script' 카테고리의 다른 글
Shell Script - 에러 처리 (0) | 2024.01.10 |
---|---|
Shell Script 기초(6) (0) | 2023.11.27 |
Shell Script 보안 - 코드 인젝션 취약점 (0) | 2023.11.24 |
Shell Script 기초(5) (0) | 2023.11.24 |
Shell Script 기초(4) (0) | 2023.11.24 |