쉘을 사용한 프로그래밍 작업으로서 서버 작업 자동화 및 운영(DevOps)를 위해서 기본적으로 익혀둘 필요가 있다.
로그가 많이 쌓여서 서버가 죽을 경우 스크립트를 생성해서 로그를 주기적으로 삭제 하도록 하는 등의 작업을 들 수 있다.
기본 문법
#!/bin/bash로 시작해야 하고 실행 권한을 가지고 있어야 한다. 파일 이름은 일반적으로 파일이름.sh와 같은 형태로작성한다.
실습
echo 함수를 이용하여 화면에 Hello bash!"를 출력 할 수 있도록 스크립트 작성
1 2 3
#!/bin/bash
echo "Hello bash"
실행 권한이 있어야 한다. chmod +x test.sh
실행은 test.sh가 있는 폴더로 들어간 후 ./test.sh 입력.
주석
#으로 시작하면 주석이다.
변수
선언
변수명=데이터로 사용한다. 띄어씌기는 허용되지 않는다.
사용
$변수명 으로 사용된다.
1 2 3 4 5 6 7
#!/bin/bash
mysql_id='root' mysql_directory='/etc/mysql'
echo $mysql_id echo $mysql_directory
실습
아이디 관련 정보 변수만들기 (실제 이름, 나이, 직업)
1 2 3 4 5 6 7
#!/bin/bash
name='koo' age=27 job='no job'
echo $name $age $job
리스트
선언
변수명=(데이터1 데이터2 데이터3 …)
사용
${변수명[인덱스번호]} 로 사용한다.
1 2 3 4 5 6 7 8 9 10
#!/bin/bash
daemons=("httpd" "mysqld" "vsftpd") echo ${daemons[1]} echo ${daemons[@]} # $daemons 배열의 모든 데이터 출력 echo ${daemons[*]} # $daemons 배열의 모든 데이터 출력 echo ${#daemons[@]} # daemons 배열 크기 출력
filelist=( $(ls) ) # 해당 쉘스크립트 실행 디렉토리의 파일 리스트를 배열로 $filelist 변수에 받음. echo ${filelist[*]}
실습
아이디 관련 정보 리스트 변수로 만들고, 각 정보 출력하기
사전에 정의된 지역 변수
1 2 3 4 5 6 7 8 9
$$ : 쉘의 프로세스 번호 $0 : 쉘스크립트 이름 $1 ~ $9 : 명령줄 인수 $* : 모든 명령줄 인수리스트 $# : 인수의 개수 $? : 최근 실행한 명령어의 종료 값 - 0 (성공), 1~125 (에러) - 126(파일이 실행가능하지 않음), - 128~255 (시그널 발생)
실습
쉘 프로세스 번호, 쉘 스크립트 이름, 명령줄 인수, 모든 명령줄 인수리스트, 인수 개수 출력해보기
1 2 3
#!/bin/bash
echo $$ $0 $1 $* $#
연산자
expr: 숫자 계산
expr를 사용하는 경우 백틱을 사용해야 함. 연산자 *와 괄호 앞에는 역슬래시를 넣어야 한. 연산자와 숫자, 변수 사이에는 space를 넣어야 함.
1 2 3 4
#!/bin/bash
num=`expr \( 10 + 20 \) / 8 - 8` echo $num
조건문 문법
기본 if 구문
명령문을 꼭 탭으로 띄워야 하는 것은 아님(then과 if 안에만 들어가 있으면 됨.)
두 인자값을 받아서 두 인자값이 다르면 differnt values 출력
1 2 3 4 5 6
#!/bin/bash
if [ $1 != $2 ] then echo 'different values' fi
조건 문에 띄어쓰기를 주의한다.
조건 작성이 다른 프로그래밍 언어와 달리 가동성이 현저히 떨어짐, 필요할때마다 찾아보길..
파일 검사
1 2 3 4 5 6 7 8 9
-e 파일명 # 파일이 존재하면 참 -d 파일명 # 파일이 디렉토리면 참 -h 파일명 # 심볼릭 링크파일 -f 파일명 # 파일이 일반파일이면 참 -r 파일명 # 파일 읽기 가능이면 참 -s 파일명 # 0 파일 크기가 0이 아니면 참 -u 파일명 # 파일이 set-user-id가 설정되면 참 -w 파일명 # 파일 쓰기 가능 상태이면 참 -x 파일명 # 파일이 실행 가능 상태이면 참
해당 파일이 있는지 없는지 출력하는 쉘 스크립트 작성해 보기
1 2 3 4
if [ -e $1 ] then echo "file exist" fi
if else
1 2 3 4 5 6 7 8
#!/bin/bash
if [ $1 == $2 ] then echo 'same values' else echo 'different values' fi
쉘 크립트 해석하기
1 2 3 4 5 6 7 8 9
#!/bin/bash
ping -c 1 192.168.0.1 1> /dev/null if [ $? == 0 ] then echo "게이트웨이 핑 성공!" else echo "게이트웨이 핑 실패!" fi
ping -c 1 192.168.0.1 1> /dev/null 응답 확인 요청 -c 1를 통해 1번만 요청, 1> /dev/null를 통해 응답 결과 표준 출력을 버림,
let eventMixin = { /** * 이벤트 구독 * 사용패턴: menu.on('select', function(item) { ... } */ on(eventName, handler) { if (!this._eventHandlers) this._eventHandlers = {}; if (!this._eventHandlers[eventName]) { this._eventHandlers[eventName] = []; } this._eventHandlers[eventName].push(handler); },
/** * 구독 취소 * 사용패턴: menu.off('select', handler) */ off(eventName, handler) { let handlers = this._eventHandlers?.[eventName]; if (!handlers) return; for (let i = 0; i < handlers.length; i++) { if (handlers[i] === handler) { handlers.splice(i--, 1); } } },
/** * 주어진 이름과 데이터를 기반으로 이벤트 생성 * 사용패턴: this.trigger('select', data1, data2); */ trigger(eventName, ...args) { if (!this._eventHandlers?.[eventName]) { return; // no handlers for that event name }
if (target.tagName != 'TD') return; // TD에서 발생한 게 아니라면 아무 작업도 하지 않습니다,
highlight(target); // 강조 함 };
functionhighlight(td) { if (selectedTd) { // 이미 강조되어있는 칸이 있다면 원상태로 바꿔줌 selectedTd.classList.remove('highlight'); } selectedTd = td; selectedTd.classList.add('highlight'); // 새로운 td를 강조 함 }
만약 td 안에 strong이라는 요소가 있어서 strong 요소를 클릭했다면 event.target은 strong 이 될 것이다.
이런 부분을 반영하여 코드를 고친다.
1 2 3 4 5 6 7 8 9
table.onclick = function(event) { let td = event.target.closest('td'); // (1)
if (!td) return; // (2)
if (!table.contains(td)) return; // (3)
highlight(td); // (4) };
event.target.closet(selector)을 활용하여 elem의 상위 요소 중 selector와 일치하는 가장 근접한 조상 요소를 반환 함.
선택한 요소가 td 안에 없으면 null을 반환함.
table 요소 밖에 td 요소를 선택했으면 return 함.
강조
이벤트 위임 활용하기
save, load, search 기능을 수행하는 버튼을 만든다고 했을때, 메뉴 전체에 핸들러를 하나 추가해주고, 각 버튼의 data-action 속성에 호출할 메서드를 할당해 주는 방법을 사용할 수 있다.
<script> document.addEventListener('click', function(event) { let id = event.target.dataset.toggleId; if (!id) return; let elem = document.getElementById(id); elem.hidden = !elem.hidden; }); </script>
직접 에러 클래스를 만든다면 name, message 프로퍼티를 만들어야 하고 가능하다면 stack 프로퍼티도 지원해야 한다. 물론 추가 프로퍼티 사용은 무엇이든 가능하다.
throw 인수엔 무엇이든 사용 가능해서 꼭 Error 객체를 상속받아야 하는것은 아니지만, Error 객체를 상속받으면 obj instance Error를 사용하여 에러 여부를 확인하는것이 가능하기 때문에 그냥 만드는 것보다 Error 객체를 상속받아서 만드는것을 추천한다.
에러 확장하기
user 정보를 읽는 readUser(json)을 만들것이다.
readUser(json)은 JSON.parse() 를 내부적으로 사용할거라 형식에 맞지 않으면 SyntaxError발생.
하지만 user라면 반드시 가져야 할 name 이나 age 같은 속성이 없을때는 이런 에러를 던지면 안된다. 따로 데이터를 검증할 것인데 이때 발생하는 에러를 validationError라고 만들것이다.
먼저 Error를 상속 받기 전에 어떤 객체인지 살펴보자.
1 2 3 4 5 6 7 8
// 자바스크립트 자체 내장 에러 클래스 Error의 '슈도 코드' classError{ constructor(message) { this.message = message; this.name = "Error"; // (name은 내장 에러 클래스마다 다릅니다.) this.stack = <callstack>; // stack은 표준은 아니지만, 대다수 환경이 지원합니다. } }
try { readUser('{잘못된 형식의 json}'); } catch (e) { if (e instanceof ReadError) { alert(e); // Original error: SyntaxError: Unexpected token b in JSON at position 1 alert("Original error: " + e.cause); } else { throw e; } }
이런 기법은 객체 지향 프로그래밍에서 널리 쓰이는 패턴임.
과제1 내장된 SyntaxError 클래스를 상속하는 FormatError 클래스를 만들어 봅시다.