if, then, fi는 쉘 스크립트에서 사용되는 제어구조를 나타내는 키워드
if는 조건문의 시작, 조건을 평가하여 해당 조건이 참인 겨웅에만 다음 then 블록의 명령어를 실행
then에는 실제로 실행시킬 명령어가 들어간다. if의 조건이 참일 경우 실행됨
fi 키워드는 조건문의 종료를 나타낸다, if조건문이 끝나는 지점을 표시해줌
smsh2.c를 통해서 단순히 명령어와 프로그램을 실행하고 프로세스를 복제하는 쉘이 아닌 좀 더 발전된 쉘을 만들어 보았었다
이번에는 백그라운드 처리를 하는 쉘을 만들어보자
우선 백그라운드 처리에 대해 생각해보려면 쉘의 로컬 변수와 전역 변수에 대해 알아보아야한다
Operation | Syntax | Notes |
---|---|---|
assignment | var=value | NO spaces |
reference | $var | e.g. $age |
delete | unset var | e.g.unset age |
stdin input | read var | e.g.read name |
list vars | set | e.g. set (shows you defined variables in the current shell) |
make global | export var | e.g.export PATH=/usr/bin:/usr/local/bin |
Variable | Value | Global? |
---|---|---|
data | "phonebook.dat" | n |
HOME | /home/jamsu | y |
TERM | "t1061" | y |
대충 이런식으로 표현된다!
인터페이스를 더 뜯어보자
- VLstore(char var, char val): var=val만큼 추가하고 바꾼다
- VLlookup(char* var): var에 대한 값을 검색한다
- VList : stdout에 리스트 업 한다
- 테이블의 자료구조는 어떻게 될까?
- linked list일수도 있고, hash table일수도 있고, tree일수도 있지만, 우린 array of structs를 사용할것이다
우리는 assign, list, retrieve 명령어를 우리의 쉘에 추가해야한다
그러나 다행히! 우리는 이런 작업을 할 수는 있다
자, 이제 본격적으로 Built-in을 추가한 smsh3라는 코드를 살펴보자!
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "smsh.h"
#include "varlib.h"
int assign(char *);
int okname(char *);
int builtin_command(char** args, int* resultp){
int rv = 0;
if(strcmp(args[0], "set") == 0){ //args[0]에 입력된 값이 set라는 문자와 같을때
VLlist(); //VLlist()를 호출하여 현재 설정된 모든 변수 출력
*resultp = 0; //resultp를 0으로 설정
rv = 1; //rv를 1로 설정, 내장 명령어로 처리됨을 나타냄
}
else if(strchr(args[0], '=') != NULL){ //args[0]에 '='문자가 포함된 경우
*resultp = assign(args[0]); //resultp에 assign함수를 호출해서 변수 할당
if(*resultp != -1){ //만약 할당 결과가 -1이 아닐경우
rv = 1; //rv를 1로 설정, 내장 명령어로 처리됨을 나타냄
}
}
else if(strcmp(args[0], "export") == 0){ //args[0]이 export인 경우
if(args[1] != NULL && okname(args[1])){
//asrgs[1]이 NULL이 아니거나, okname(args[1])이 true인 경우
*resultp = VLexport(args[1]);
//VLexport(args[1])을 호출하여 변수를 환경변수로 내보내고
//결과는 resultp에 저장
}
else{
*resultp = 1; //*resultp를 1로 설정
}
rv = 1; //마지막으로 rv를 1로 설정
}
return rv; //rv값 반환
}
int assign(char* str){ //assign 함수를 정의
char* cp;
int rv;
cp = strchr(str, '='); //문자열 str에서 '='이라는 문자를 찾는다
//string.h
*cp = '\0'; //'='문자를 '\0'으로 변경하여 변수 이름과 값을 분리
rv = (okname(str) ? VLstore(str, cp+1) : -1);
//oknames함수를 호출하여 변수 이름이 유효한지 확인
//VLstore(str, cp+1)를 호출하여 변수를 저장
*cp = '='; //'='문자를 다시 원래의 값으로 변경
return rv; // 변수의 할당 결과인 rv를 반환
}
int okname(char* str){
char* cp;
for(cp = str; *cp; cp++){ //반복문 사용, cp값을 증가시키며 가는거임
if((isdigit(*cp) && cp==str)||!(isalnum(*cp)||*cp=='_')){
//그렇지 않은경우 조건을 확인하여 알파벳 또는 숫자 또는 '_'문자인지 확인
return 0;
}
}
return (cp != str)
}
자, 우리는 builtin command를 만들려고하고있다
여기선 builtin_command, assign, okname 함수가 정의되어있다각각의 전반적 기능을 알아볼까?
- builtin_command 함수: str이 유효한 변수 이름인지 검사해주는 함수
- assign 함수: str에 대해 변수 이름과 값을 분리, 유효한 변수 이름인 경우, 변수에 값을 할당
문자열에서 변수 이름과 값을 분리, 유효한 변수 이름인 경우 변수에 값을 할당하는 기능- okname 함수: str이 유효한 변수 이름인지를 확인하는 함수
int VLstore(char* name, char* val);
char* VLlookup(char* name);
int VLexport(char*name);
void VLlist();
int VLenviron2table(char* env[]);
char** VLtable2environ();
새롭게 정의된 헤더파일이다
이런 함수가 있다~ 정도만 알고 넘어가자
#include <stdio.h>
#include <stdlib.h>
#include "varlib.h"
#include <string.h>
#define MAXVARS 200
struct var{
char* str;
int global;
};
static struct var tab[MAXVARS];
static char *new_string(char*, char*);
static struct var* find_item(char*, int);
int VLstore(char* name, char* val){
struct var *itemp;
char *s;
int rv = 1;
if((itemp=find_item(name, 1)) != NULL && (s=new_string(name, val))!=NULL){
//find_item함수를 사용해 변수이름 name과 일치하는 변수 항목을 찾는다
//변수 항목이 존재하며, new_string(name, val)함수를 호출해서 새로운 문자열을 생성
if(itemp->str)
//생셩된 문자열이 유효하다면?
free(itemp->str); //문자열 해제
itemp->str = s; //새로운 문자열 할당
rv = 0; //rv값에 0 대입
}
return rv; //rv값을 반환
}
char* new_string(char* name, char* val){ //new_string함수 정의
char* retval;
retval = malloc(strlen(name)+strlen(val)+2);
// 동적배열로 retval배열 생성
if(retval != NULL) //retval이 NULL이 아닌 경우,
sprintf(retval, "%s=%s", name, val); // name과 val을 이름=값 형식으로 조합하여 retval 저장
return retval; //retval 반환
}
char* VLlookup(char* name){
struct var* itemp;
if((itemp = find_item(name, 0)) != NULL){ //struct var 구조체를 가리키는 포인터
return itemp->str+1+strlen(name); //검색된 변수의 str 멤버를 가리키는 포인터에
//1+strlen(name)을 더한 값을 반환
//값 부분을 가리키는 포인터 반환
}
}
int VLexport(char* name){ //반환값은 성공 여부를 나타내는 정수
struct var* itemp;
int rv = 1; //rv는 반환 값을 저장하는 변수. 초기값은 1
if((itemp = find_item(name, 0)) != NULL){ //find_item함수를 사용해서 name에 해당하는 변수 검색
//검색결과가 NULL이 아닌경우 조건이 참이 된다
itemp->global = 1; //itemp->global 멤버를 1로 설정하여 전역 변수로 설정
rv = 0; //rv값, 즉 반환값을 0으로 설정해서 성공했음을 나타냄
}
else if(VLstore(name, "") == 1){ //name에 빈 값할당, 반환값이 1인경우 조건이 참
rv = VLexport(name); //VLexport함수를 재귀적으로 호출해서 변수를 export
//변수가 없는 경우에도 export가능하게 됨
}
return rv; //반환 값을 rv로 반환
}
static struct var* find_item(char* name, int first_blank){
int i; //반복문에서 사용할 변수 i 선언
int len = strlen(name); //strlen을 사용해서 name문자열의 길이를 len에 저장
char *s; //s라는 포인터 변수 선언
for(i = 0; i < MAXVARS && tab[i].str != NULL; i++){
// MAXVARS값만큼 i값이증가, tab[i].str값이 비어있지 않을때
s = tab[i].str; //tab[i].str값을 s에 저장
if(strncmp(s, name, len) == 0 && s[len] == '='){
//s와 name을 len만큼 비교, 비교 결과가 0인경우, s[len]이 =인 경우 조건이 참이 된다
return &tab[i]; //tab배열의 i번째 요소의 주소를 반환, 해당 값을 찾았음을 표시
}
}
if(i < MAXVARS && first_blank){
//변수 검색을 마쳤는데도 i가 MAXVARS보다 작고 firstblank가 참인 경우
return &tab[i]; //tab배열의 i번째 요소의 주소를 반환
}
return NULL; //위의 조건에 해당하지 않으면, NULL을 반환해서 변수를 못찾았음을 나타냄
}
void VLlist(){
int i; //반복문에서 사용할 변수
for(i = 0; i < MAXVARS && tab[i].str != NULL; i++){
//tab 배열의 순회
if(tab[i].global){ //tab[i]의 global 멤버가 참인 경우
printf(" *%s\n", tab[i].str); //tab[i].str을 출력, *으로 글로벌 변수임을 나타냄
}
else{ //참이 아닌경우
printf(" %s\n", tab[i].str); //tab[i].str을 출력
}
}
}
int VLenviron2table(char* env[]){
int i; //반복문에서 사용할 변수
char* newstring; //새로운 문자열 포인터
for(i = 0; env[i] != NULL; i++){ //env[i]배열을 증가시키면서 NULL값이 찾아질때까지
//즉, 종료될때까지
if(i == MAXVARS){ //만약 i값이 MAXVARS와 같다면,
return 0; //프로그램 종료
}
newstring = malloc(1+strlen(env[i])); //newstring이라는 배열을 동적배열로 설정
if(newstring == NULL){ //newstring이 비어있으면
return 0; //프로그램 종료
}
strcpy(newstring, env[i]); //env[i]번째 문자열이 newstring과 같다면
tab[i].str = newstring; //newstring값을 tab[i].str에 할당
tab[i].global = 1; //tab배열의 i번째 요소의 global 멤버를 1로 설정
}
while(i < MAXVARS){ //i값이 MAXVARS값보다 작은 동안 반복
tab[i].str = NULL; //tab의 i번째 요소의 str멤버를 NULL로 설정
tab[i++].global = 0; //tab배열의 i번째 요소의 global멤버를 0으로 설정, i를 증가
}
return 1; //1을 반환하여 성공을 나타낸다
}
char** VLtable2environ(){
int i, j; //반복문에서 사용될 번수
int n = 0; //n을 0으로 초기화
char** envtab; //envtab 이중포인터 선언
for(i = 0; i < MAXVARS && tab[i].str != NULL; i++){
//반복문 사용, tab배열 순회, i가 MAXVARS보다 작고, tab[i].str이 NULL이 아닌 동안 반복
if(tab[i].global == 1){
//tab[i]의 global 멤버가 1인경우
n++; //n값을 증가
}
}
envtab = (char**)malloc((n+1)*sizeof(char*)); //동적배열 설정
if(envtab == NULL){ //envtab이 비어있는 경우
return NULL; //NULL값 반환, 함수 종료
}
for(i=0, j=0; i < MAXVARS && tab[i].str != NULL; i++){
// tab배열을 순회, tab[i].str이 NULL이 아닐떄까지 반복
if(tab[i].global == 1){ //tab[i]의 global 멤버가 1인경우
envtab[j++] = tab[i].str; //envtab을 증가시키며 tab[i].str의 요소를 순차적으로 넣음
}
}
envtab[j] = NULL; //마지막 요소에 NULL할당
return envtab; //envtab을 반환
}
- VLstore: 주어진 변수 이름과 값을 이용하여 변수를 저장. 변수 항목이 존재하고 새로운 문자열이 유효하게 생성되면, 기존 변수 값이 갱신되고, 반환값이 0 이 됨. 그렇지 않은 경우, 반환값은 1
- new_string: name과 val을 조합하여 "이름=값"형식의 새로운 문자열을 생성, 해당 문자열을 가리기는 포인터를 반환
- VLookup: 주어진 name에 해당하는 변수를 검색, 검색된 변수의 값을 가리키는 포인터를 반환
- VLexport: name에 해당하는 변수를 export하고, export가 성공했는지 여부를 반환
- first_blank: name에 해당하는 변수를 tab 배열에서 검색해서 해당 변수를 가리키는 포인터를 반환, first_blank가 참인 경우, 새로운 변수를 할당할 공간을 나타내는 포인터 반환
- VLlist: tab 배열의 내용을 출력하는 함수, global 멤버가 참인경우, *을 붙여서 글로벌 변수임을 표시, 그렇지 않은경우 공백을 붙여 출력
- VLenviron2table: 전역변수 배열 env를 tab 배열로 변환, 전역변수값을 newstring에 복사, tab 배열의 각 요소에 할당. tab 배열 요소를 초기화
- VLtable2environ: tab 배열에서 global 멤버가 1인 요소들을 추출하여 이중 포인터 envtab에 저장하는 함수. 반환된 envtab은 환경 변수를 나타내는 char*배열로 사용가능
점점 갈수록 코드가 복잡해진다...
그러나 아직 코드를 한번 더 수정해볼 수 있다...!
다음시간에 알아보자