1. 스크립트
1) 스크립트란?
명령어를 순서대로 자동 실행하도록 작성한 텍스트 파일을 말한다. 반복적인 작업을 자동화하여 효율을 높이는 데 많이 이용된다.
- 컴파일 언어 : .c -> gcc 등으로 바이너리 파일로 컴파일 후 실행 ex) C++
- 인터프리터 언어 : 소스 코드를 인터프리터가 한 줄씩 해석하여 실행 ex) Python, Bash
# cat /etc/shells 명령어를 통해 시스템에 설치된 쉘(interpreter) 목록을 확인할 수 있다.
2) 쉘 종류
- sh : 오리지날 쉘, 부팅시에 실행되는 스크립트는 sh를 이용
- bash : 리눅스에 기본 탑재, 왠만한 배포판에는 bash를 이용중
- csh : c언어 기능을 탑재
- tcsh : c쉘이 발전
- ksh : aix , hp_ux 쪽에서 사용
- zsh : 그 이후 개발된 쉘
- fish : 그 이후 개발된 쉘
- dash : ubuntu 6.06 이후 탑재 [사용자 기능이 없어서 bash 기반으로 작성된 스크립트가 동작을 안할수 있음 ]
3) Shebang (#!)
참고로 '셔뱅'이라 읽는다...
#!/usr/bin/bash
스크립트 상단에 선언하는 인터프리터 지정 지시자로 첫 2바이트를 OS가 감지하여 뒤에 오는 경로로 실행해준다.
#로 시작하지만 주석이 아니다!
명령어로 실행하는 경우(bash run.sh)에는 Shebang이 없어도 되지만, 직접 실행하거나 자동화할 때는 적어줘야 한다.
💡 리눅스에서의 확장자
리눅스에서 파일 이름에 지정해주는 확장자는 OS 수준에서는 아무런 의미가 없는 단순 관례일 뿐이다.
실행 궈한이 있고 인터프리터가 지정되어 있다면 확장자 없이도 실행이 가능하다.
그렇기에 외부에서 받아온 파일일 경우 # file 명령어로 파일 타입을 꼭 확인하는 습관을 들이자.
6) 공용 스크립트 디렉토리 구성
다른 사용자들과 공용으로 사용할 작업 디렉토리의 경우 /usr/local/bin 밑에 새로운 디렉토리를 만들어서 작성한다.
7) 명령어 실행시의 순서
- hash: 캐시된 명령어 우선
- alias: 별칭 우선
- $PATH 검색: PATH에 등록된 디렉토리를 하나씩 순차적으로 돌면서 ls 명령어를 찾고 실행. 가장 처음으로 찾아낸걸 실행. 스크립트용 디렉토리를 항상 뒤에 추가, 같은 이름이 있는지 먼저 검수한 뒤 파일을 작성해야 함
2. 반복문
1) for
기본 문법
for 변수 in 리스트; do
명령어
done
예제
for i in 1 2 3 4 ; do
> echo $i
> done
1
2
3
4
- $i : 현재 반복 변수 값을 참조
awk를 이용한 예제
# seq -w 1 100
: 숫자 앞에 0을 붙여서 고정 길이를 유지해 001부터 100까지 숫자 리스트 생성
2) while
기본 문법
while true; do
명령어
done
주의사항
- while true는 무한 루프이기 때문에 sleep 구문을 꼭 넣어줘야 CPU 사용률 폭주를 막을 수 있다.
- 루프 자체는 top 명령어에서 돌아가고 있는지 확인이 어려울 수 있다.
- Ctrl+C를 누르기 전까지는 무한정 돈다는 단점이 있다.
3) 두 반복문의 결합 스크립트
#!/usr/bin/bash
hosts_file="hosts_output.txt"
prefix="13.13.13."
domain_name=".example.com"
start=1
end=254
current_ip=${start}
while [ ${current_ip} -le ${end} ]; do
for host_number in $(seq ${current_ip} ${end}); do
echo "${prefix}${host_number} host${host_number}${domain_name} host${host_number}" >> $hosts_file
done
current_ip=$((current_ip + 1))
done
echo "hosts file update"
- while: IP 범위 (start~end)를 컨트롤
- for: 각 host_number에 대해 한 줄씩 생성
- >>: 결과를 파일에 덧붙이기
- ${}: 변수 참조
- $(()): 산술 연산 (e.g. +1)
- $(): 명령어 결과 치환 (e.g. seq)
출력 예
13.13.13.1 host1.example.com host1
13.13.13.2 host2.example.com host2
...
3. 조건문
기본 문법
if [ 조건 ]; then
명령어
elif [ 다른 조건 ]; then
명령어
else
명령어
fi
if [ $? = 0 ]; then
: 직전에 실행한 명령어의 종료 상태($?)가 0인지 확인
성공이면 0, 실패면 0이 아님
4. 원격지 접근 자동화 스크립트
보안성과 확장성을 고려한 SSH 키 자동 생성, 배포 및 테스트까지 수행하는 스크립트를 구현한다.
요구사항
- 비밀번호 키가 남아 있으면 안된다.
- 이후 접속에 비밀번호 입력안하고 사용 가능해야 한다.
- sshpass가 없다면 설치 -> if [ -f /usr/bin/sshpass ]
- 파일이 있으면 그냥 패스! 없으면 설치 후 진행
- vm2.exmaple.com에 인증키를 배포하는 단 하나의 파일로 구성
한글로 작성한 수도코드
- sshpass 유무 확인
- 인증키 유무 확인
- 인증키 생성
- 비밀번호 파일 구성
- 내보내기
- 비밀번호 파일 삭제
- 디렉토리 삭제
- 정상적으로 동작하는지 테스트 : ssh root@10.10.10.20 "hostname"
구현 코드
#!/usr/bin/bash
# Custom value
#TARGET_IP="10.10.10.20"
#TARGET_USER="root"
#PASS_FILE="password"
#PASS_TEXT="rocky"
#rm -rf ${HOME}/.ssh
#0. input value
while true; do
read -p "Enter target ip: " TARGET_IP
read -p "Enter target user: " TARGET_USER
echo ""
echo "[CHECK] IP:${TARGET_IP}, USER_NAME:${TARGET_USER}"
read -p "is this correct? (yes/no): " confirm
if [[ "$confirm" == "yes" ]]; then
break
else
echo "[INFO] please re-check IP and username"
fi
done
while true ; do
read -sp "Enter ssh password: " pass_text1
echo ""
read -sp "Re-enter ssh password: " pass_text2
echo ""
if [ "$pass_text1" == "$pass_text2" ]; then
PASS_TEXT=${pass_text1}
break
else
echo "[WARN] password do not match" >&2
fi
done
PASS_FILE="password"
#1. check if sshpass is installed
if [ ! -f /usr/bin/sshpass ]; then
echo "[INFO] sshpass command is not found. installing.."
dnf -y install sshpass || { echo "[ERROR] could not install sshpass" >&2 ; exit 1; }
fi
#2. sshkey if not exist
if [ ! -f "${HOME}/.ssh/id_rsa" ]; then
echo "[INFO] not found id_rsa eky. making a new id_rsa.."
ssh-keygen -f ${HOME}/.ssh/id_rsa -N '' -t rsa -b 4096 > /dev/null
fi
#3. make password file
echo ${PASS_TEXT} > ${PASS_FILE}
chmod 400 ${PASS_FILE}
#4. copy ssh key using password
sshpass -f ${PASS_FILE} ssh-copy-id -o StrictHostKeyChecking=no -f ${TARGET_USER}@${TARGET_IP}
#5. delete password file safely
if [ -f ${PASS_FILE} ; then
echo "[INFO] delte password file"
shred -u ${PASS_FILE}
fi
#6. check remote host ssh connection words (run hostname check)
echo "[INFO] test ssh connection"
ssh -o StrictHostKeyChecking=no ${TARGET_USER}@${TARGET_IP} 'hostname' > /dev/null
if [ $? -eq 0 ]; then
echo "[INFO] ssh test OK"
else
echo "[INFO] ssh test FAIL"
fi
unset TARGET_IP
unset TARGET_USER
unset PASS_FILE
unset PASS_TEXT
# read -p "텍스트" 변수
: 입력 받은 값을 변수에 저장
# read -sp "텍스트" 변수
: 입력값을 화면에 표시하지 않음 (s = silent)
# ssh-keygen
: SSH 키 생성
# ssh-copy-id [사용자계정]@[IP주소]
: 패스워드를 적을 때 IP 주소에 해당하는 서버의 비밀번호를 자동 입력
# ssh-copy-id -o StrictHostKeyChecking=no root@10.10.10.20
: 등록은 가능 하지만 여전히 비밀번호를 한번 입력해야 함
권한이 400인 password 파일 생성
-> sshpass -f password ssh-copy-id -o StrictHostKeyChecking=no root@10.10.10.20
: 비밀번호 입력 없이 수행 ssh 접근
작업 이후에는 비밀번호로 사용된 파일은 삭제하자.
# shred -u 파일명
: 내용을 다른걸로 덮어씌우고 삭제하는 방식으로 민감한 정보가 들어 있는 파일을 삭제할 때 주로 사용됨
💡 Bash 변수 대소문자 네이밍 관례
수업을 듣다가 왜 처음에 정의한 변수는 대문자로만 적고, 입력으로 받은 값을 저장하는 변수는 소문자로 적는지에 대한 의문이 들었다. 문법이 정해져 있는 건가 했으나 단순 관례라고 한다.
스크립트 내에서 한정된 범위에서만 사용되는 로컬/임시 변수는 소문자로, 외부에서 참조할 수도 있고 스크립트에서 전역적으로 쓰이는 전역 상수/환경 변수는 대문자로 쓰는 것이 일반적인 관례이다.
✍️ 하루 회고
오늘은 리눅스 스크립트 작성과 관련된 내용을 중심으로 학습했다.
고등학교 때부터 익숙하게 다뤘던 반복문과 조건문이 나와서 비교적 수월하게 따라갈 수 있었다.
이번 강의를 통해 스크립트가 단순한 명령어의 나열이 아니라, 반복적이고 불필요한 작업을 줄여주는 강력한 도구라는 점을 다시금 실감했다. 앞으로도 스크립트 작성법을 꾸준히 익혀서, 자동화를 통해 업무 효율을 높일 수 있는 엔지니어로 성장하고 싶다.
'TIL' 카테고리의 다른 글
[에스넷시스템 부트캠프] TIL Day 21 - Ansible 문법 (2) | 2025.06.19 |
---|---|
[에스넷시스템 부트캠프] TIL Day 20 - IaC, Ansible 기초 (0) | 2025.06.17 |
[에스넷시스템 부트캠프] TIL Day 18 - Crontab, autofs, swap (0) | 2025.06.14 |
[에스넷시스템 부트캠프] TIL Day 17 - NTP, DNF (0) | 2025.06.12 |
[에스넷시스템 부트캠프] TIL Day 16 - LVM (0) | 2025.06.12 |